The Postman Collection is a fantastic tool for collaborating with your team throughout the API design, development, testing, and documentation stages. But that’s not all—Postman’s API builder also lets you create and manage your APIs with ease.
In a previous post, we talked about measuring a collection’s quality with Spectral. In this post, we’ll show you how to do the same thing with an API, using the same governance rules that we use at Postman.
Postman’s API governance rules
When designing an API, it’s crucial to establish rules to ensure your API will be consistent with design guidelines and also easy for users to understand and use.
The APIs we design at Postman follow level 2 of the Richardson Maturity Model. This means we expose resources and use proper HTTP verbs to manage our APIs. We expanded on this maturity model with additional Spectral rules to improve consistency and quality in our APIs.
At Postman, the design process of REST APIs eventually creates an OpenAPI document. We check several governance rules to evaluate if the API meets our quality standards.
These are some of the governance rules we apply to our OpenAPI definitions during the API design process:
Governance rule | Description | Example |
---|---|---|
Semantic versioning | The info.version property must contain a valid Semantic Versioning value. |
"version": "1.0.1" |
Path resource names in kebab-case and path parameters in camelCase | API resource names must be in kebab-case and parameters in camelCase. | /pull-requests/{pullRequestId} |
Resource nesting not allowed | You cannot nest two API resource names. You must separate them with a path parameter or a variable. | /pull-requests/{pullRequestId}/operations /pull-requests-operations |
No version in resource names | We use HTTP header versioning, instead of path or query string versioning. For example, to use the Postman APIs API, users must pass an Accept: application/vnd.api.v10+json header. Otherwise, the API defaults to the previous version. |
/apis |
Resource names are plural nouns | API resource names normally expose several resources, so they must be plural nouns. | /collections /users |
Date-time format | Dates and times must follow the conventions in RFC-3339 and use the ISO-8601 standard. | 2023-06-16T06:43:34-07:00 |
ids parameter format | If the API receives an ids query parameter, the format must be a list of comma-separated ID values. |
ids=1,2,3 |
cursor parameter format | We highly recommend using cursor pagination for new APIs. The cursor query string parameter indicates a pagination cursor. Must be a string type. |
/items?cursor=abcd |
nextCursor attribute format | The nextCursor response field is the value of a cursor representing the next page in a data set. Must be a string type. |
"nextCursor": "abcd" |
limit parameter format | When using pagination, the limit parameter indicates the maximum number of items to retrieve. Must be an integer type. The documentation should establish clear default and max values for this parameter. |
/items?limit=10 |
sort parameter format | The sort parameter for pagination is optional and determines the attribute used to sort the results. Must be a string type. |
/items?sort=id |
direction parameter format | The direction parameter is optional and determines a sorting direction for results. Must use the asc (ascending) and desc (descending) enum values. |
/items?direction=asc |
since and until parameter format | The since and until query string parameters, when used for optional pagination, get all items created between a specific set of timestamps. Must be string types and use date-time format. |
/items?since=2023-06-16T06:43:34-07:00 /items?until=2023-06-16T06:43:34-07:00 |
At least one 2xx or 3xx response | Every operation must have at least one 2xx or 3xx response. | |
Unique operationId | Every operation must have a unique operationId value. | |
PATCH format | PATCH operations update resources and must accept a JSON Patch document that describes what to update. The accepted body must be in the application/json-patch+json Media Type. |
|
API description is mandatory | The info.description property must be present. At Postman, we manually review the description to ensure it meets our standards. |
|
All parameters have examples | All parameters must include at least one example property. |
example: userId |
All object properties have examples | Except for individual parameter examples, objects must include at least one example of the entire object. | |
Existing 401 response | Responses in endpoints that require authentication must contain an HTTP 401 Unauthorized response. |
|
Existing 404 response | Responses must contain an HTTP 404 Not Found response where applicable. For instance, if the endpoint is a detail endpoint, such as /collections/{collectionId} , then the 404 response is mandatory. For list endpoints like /collections , this isn’t mandatory. |
|
Path parameters in curly braces | Path variables must be inside of curly braces ({} ). |
/collections/{collectionId} |
Path variables are defined as parameters | If the path contains path variables, they must be defined in the path’s parameters section. |
|
Query parameters in camelCase | Query parameter names must be in camelCase. | /schemas?objectId=XXX |
Error response format | Error responses must follow the RFC 7807 Problem Details format. The error response must contain the type , title , and detail properties. |
|
Mandatory path verb description and summary | Request and response path verbs must have a description and a summary. |
Most of these rules will result in an error response during the validation process. This error response prevents the API from being exposed with rule violations. Some rules are warnings that require human review. You can find the Spectral ruleset for this ruleset in our spectral-ruleset GitHub repository
We use additional custom internal governance rules that ensure our internal workflows in API exposure work properly. We also track previous versions of an OpenAPI spec and also validate the spec to check that:
- There are no breaking changes in the new version.
- The version number is higher than the currently exposed version number.
- We review manually the whole definition (or the changes from the previous version) to ensure consistency.
- API owner contact information must be included in the OpenAPI spec to facilitate contacting them for any questions.
We also use guidelines, recommendations, and reusable OpenAPI code for:
- Pagination (cursor and limit/offset pagination)
- Error responses
- Extended resources
- Polymorphism
- Documentation
- Rate-limiting headers
- Task resources (execute operations on a resource)
If you design your API definition following these recommendations, this can help ensure consistency and improved usability.
Programmatically check your API’s governance rules
If you have a Postman Enterprise plan, you can define your governance rules directly in Postman. These rules apply in the API builder and help indicate any possible problems with the API definition in real-time.
You can also create an automated process that checks the API, then integrate that process into your CI flow. In this case, leverage the Postman API to retrieve your API code and use Spectral to validate it.
Next, we’ll show you how to create an automation tool that reviews an API’s quality using the Postman API, Spectral CLI, and jq. You can find this code in Postman’s postman-api-spectral-linter GitHub repository.
How it works
When you run the tool’s shell script (Bash) command, it:
- Gets the API schema with the Postman API’s Get an API endpoint.
- Gets the API’s bundled OpenAPI definition with the Get a schema. Although Postman enables you to create maintainable multi-file OpenAPI definitions, in this case, we need the definition as a single, bundled file.
- Uses Spectral CLI to validate our ruleset against the API.
- Checks whether the Spectral results contain errors and returns them the script’s exit status. If any of the rules fail, then the command fails.
- Optionally, the script uses the Create an API comment endpoint to add a comment to the API. Postman sends anyone watching the API a notification containing the failing rules.
Here’s the script’s main code:
# Perform a curl request against the Postman API to retrieve the API schema. Save the request body in a file called _api.json
curl -s -H "Accept: application/vnd.api.v10+json" -H "x-api-key: $API_KEY" https://api.postman.com/apis/$API_UID?include=schemas,collections,versions,gitInfo | jq . > _api.json
# extract the schema ID from the first schemas list in the response with jq and save it in a variable
SCHEMA_ID=$(jq -r '.schemas[0].id' _api.json)
# get the bundled schema in a file
curl -s -H "Accept: application/vnd.api.v10+json" -H "x-api-key: $API_KEY" https://api.postman.com/apis/$API_UID/schemas/$SCHEMA_ID?bundled=true | jq . > _api.json
jq -r '.content' _api.json > _api.yaml
# Lint the API using spectral. Save the result in a JSON file
spectral lint _api.yaml --ruleset $RULES_PATH -f json --quiet > _result.json
# search for errors (severity=0) in the result file. If there are errors, exit with a non-zero status code
jq '.[] | select(.severity==0)' -e _result.json >/dev/null
if [ $? -eq 0 ]
then
# if COMMENT is true, call the Postman API to create a comment with the result
if [ $COMMENT = true ]
then
COMMENT_BODY=$(jq -r 'map( .code + ": " + .message )' _result.json)
echo $COMMENT_BODY > _errors.json
# build a valid json file with {"body": "comment body"} with jq
cat _errors.json | jq -Rs '{body: .}' > _comment.json
curl -s -H "x-api-key: $API_KEY" -H "Content-Type: application/json" -X POST --data-binary "@./_comment.json" https://api.postman.com/apis/$API_UID/comments >/dev/null
echo "Comment created on the API"
fi
exit 1
fi
About this code
- At line 2, we use the Postman API to get the API’s information, including its schemas.
- At line 5, we get the schema ID from the previous JSON response and save it as a variable.
- At line 8, we use the Postman API to get the API’s bundled contents.
- At line 10 we extract the content (the OpenAPI definition) and save it into a file.
- At line 13, we use Spectral CLI to check the rules against the ruleset file. Then we dump the results into a temporary _result.json file in JSON format.
- At line 16, we search for errors (severity 0) in the results with jq.
- At line 18, the tool checks if the previous command succeeded with an execution status of
0
. In that case:- If the argument
-c
was passed, we use the Postman API’s Create an API comment endpoint to create a comment with the errors in the comment body. - We return an error execution status of
1
. You can use this status code in scripts to evaluate if the collection contains any errors.
- If the argument
Use the tool
Once you clone the GitHub repository, run the shell script with the following command:
./linter.sh -a <api_uid> [-k <postman_api_key>] [-r path_to_ruleset_file] [-c]
For more details, see the repository’s README.md file.
In this command, the postman_api_key
parameter defaults to a POSTMAN_API_KEY
environment variable. The path_to_ruleset_file defaults
parameter uses the spectral-ruleset/ruleset.yaml
ruleset. You can find this ruleset in the postman-api-spectral-linter GitHub repository. You can also extend it by adding new rules or turning off any existing rules you don’t need.
Summary
- The Postman API offers full CRUD capabilities, making it easy to manage your APIs. With a valid API key, you can effortlessly retrieve an API’s OpenAPI definition using the Postman API.
- Define Spectral rules to ensure your API meets your quality standards.
- Automatically check your API governance rules with the Spectral CLI.
- Collaborate on API design using the Postman API Builder.
- Enhance collaboration by adding comments to your APIs with the Postman API.
- Integrate quality checks into your CI/CD workflows to boost overall API quality.
Next steps
- Define your own rules that meet the quality needs you want for your APIs. You can extend the example rules provided in this post.
- Use Postman’s postman-api-spectral-linter tool to automatically notify users when they don’t meet the quality standards and help your team maintain great APIs.
Do you have any use cases related to API quality? We would love to hear about it! Let us know in the comments below!