Before I ride off into the sunset, I thought I should share with you some of the tools that I use in REST API reviews, in case these might be helpful to anyone.
I know we have nearly completed the transition to TypeSpec across all of Azure, but I still prefer reviewing the OpenAPI as I am very familiar with the OpenAPI standard and there are some good tools available to aid in the review. And TypeSpec, for all its benefits, does not really support reviewing a REST API as well as it should, though I understand there is work underway to improve this (mainly highlighting suppressions / deviations from TypeSpec Azure Core).
The main tool I use in REST API reviews is the Spectral linter with a ruleset specifically designed to check for compliance with the Azure REST API Guidelines.
Spectral linter: https://github.com/stoplightio/spectral azure-api-style-guide: https://github.com/Azure/azure-api-style-guide
Before I even start looking at a REST API, I run spectral with the azure ruleset on it. The README in the azure-api-style-guide repo explains how to install Spectral and configure it to use the Azure ruleset.
>spectral lint specification/purviewdatagovernance/data-plane/DataEstateHealth/preview/2024-02-01-preview/DataEstateHealthApiService.json
/Users/mikekistler/Projects/Azure/azure-rest-api-specs/specification/purviewdatagovernance/data-plane/DataEstateHealth/preview/2024-02-01-preview/DataEstateHealthApiService.json
29:21 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. x-ms-parameterized-host.parameters[0].x-ms-enum
49:18 warning az-security-definition-description Security definition should have a description. securityDefinitions.OAuth2Auth
60:27 information az-put-path Put on a path that does not end with a path parameter is uncommon. paths./analytics/schedule
102:11 warning az-parameter-description Parameter should have a description. paths./analytics/schedule.put.parameters[1]
151:23 warning az-response-body-type Response body schema must not be a bare array. paths./analytics/schedule/getjoblogs.get.responses[200].schema.type
178:13 warning az-pagination-response Operation might be pageable. Consider adding the x-ms-pageable extension. paths./controls.get
271:11 warning az-parameter-description Parameter should have a description. paths./controls/{id}.put.parameters[2]
352:13 warning az-pagination-response Operation might be pageable. Consider adding the x-ms-pageable extension. paths./controls/assessments.get
445:11 warning az-parameter-description Parameter should have a description. paths./controls/assessments/{id}.put.parameters[2]
535:17 warning az-success-response-body All success responses except 204 should define a response body. paths./controls/reset.post.responses[200]
558:26 information az-put-path Put on a path that does not end with a path parameter is uncommon. paths./controls/schedule
600:11 warning az-parameter-description Parameter should have a description. paths./controls/schedule.put.parameters[1]
671:11 warning az-parameter-description Parameter should have a description. paths./scores/query.post.parameters[2]
684:23 warning az-response-body-type Response body schema must not be a bare array. paths./scores/query.post.responses[200].schema.type
718:11 warning az-parameter-description Parameter should have a description. paths./settings/mpe.post.parameters[1]
728:17 warning az-post-201-response Using post for a create operation is discouraged. paths./settings/mpe.post.responses[201]
762:11 warning az-parameter-description Parameter should have a description. paths./settings/mpe/exists.post.parameters[1]
778:17 warning az-post-201-response Using post for a create operation is discouraged. paths./settings/mpe/exists.post.responses[201]
846:11 warning az-parameter-description Parameter should have a description. paths./settings/storageConfig.post.parameters[1]
856:17 warning az-post-201-response Using post for a create operation is discouraged. paths./settings/storageConfig.post.responses[201]
890:11 warning az-parameter-description Parameter should have a description. paths./settings/storageConfig/connectivity.post.parameters[1]
940:16 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.AggregatedScore.properties.time
1041:21 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.AnalyticsScheduleRequest.properties.startTime
1046:19 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.AnalyticsScheduleRequest.properties.endTime
1193:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.AssessmentRuleSeverity.x-ms-enum.values[0]
1221:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.AssessmentTargetEntityType.x-ms-enum.values[0]
1324:23 information az-schema-names-convention Schema name should be Pascal case. definitions.Azure.Core.uuid
1436:25 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.Control.properties.lastRefreshed
1764:32 warning az-boolean-naming-convention Do not use "is" prefix in names of boolean values definitions.NetworkProperties.properties.isManagedVnetEnabled
1927:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.RuleCheckPoint.x-ms-enum.values[0]
1970:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.RuleGroupOperator.x-ms-enum.values[0]
2028:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.RuleOperator.x-ms-enum.values[0]
2156:21 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.Schedule.properties.startTime
2161:19 warning az-datetime-naming-convention Use an "At" suffix in names of date-time values. definitions.Schedule.properties.endTime
2265:11 warning az-ms-enum-descriptions Include descriptions for all values in the x-ms-enum extension. definitions.ScheduleWeekDay.x-ms-enum.values[0]
2442:23 warning az-property-names-convention Property name should be camel case.
Some, perhaps many, of the issues reported are benign and not worth raising in a review. For example: Operation might be pageable. Consider adding the x-ms-pageable extension.
This is a hold-over from the auto rest days. We should probably update the ruleset to remove this but never found the time.
Some rules check for naming conventions, and these are helpful to guide services before their first GA, but afterwards are just noise as the service should follow whatever conventions it has already established. In this case, rules can be disabled per service using the override mechanism built into Spectral. I've created a collection of overrides over the years in this file:
https://github.com/Azure/azure-api-style-guide/blob/dogfood/azure-spectral.yaml
To illustrate how reviewing the OpenAPI can be useful, I want to highlight one of messages in the output above, which is for a Purview Governance PR we reviewed recently:
151:23 warning az-response-body-type Response body schema must not be a bare array. paths./analytics/schedule/getjoblogs.get.responses[200].schema.type
In this case, a "list" operation is returning an array at the top level of the response, which does not comply with our Azure REST API Guidelines, specifically
✅ DO structure the response to a list operation as an object with a top-level array field containing the set (or subset) of resources.
The TypeSpec definition of this operation does not appear to use a standard Azure Core operation template but somehow does not need a #suppress (not sure why).
@doc("Get analytics job logs for the global schedule.")
@route("/analytics/schedule/getjoblogs")
@get
getJobLogs(...ApiVersionParameter): AnalyticsJobLogsResponse | ErrorResponse;Spectral runs on the entire OpenAPI file, which means it can flag things that were reviewed in previous sessions. So I built a wrapper around spectral to filter out messages from the previous API version. I have this tool checked into a "bin" repo on GitHub where I store my personal tools. This one is called "lint-diff.py", and it takes the prior and new OpenAPI files as input.
lint-diff.py: https://github.com/mikekistler/bin/blob/main/lint-diff.py
Here's an example of that tool -- this is for PR #41333:
>f=specification/translation/data-plane/DocumentTranslation/stable/2026-03-01/openapi.json
>f0=specification/translation/data-plane/DocumentTranslation/preview/2025-12-01-preview/openapi.json
>lint-diff.py $f0 $f
493 az-formdata hint Check for appropriate use of formData parameters.
Another wrapper I built for Spectral creates a summary of the messages, so that I can focus on the big issues but not miss the isolated but important ones. This one is called "lint-summary.py".
lint-summary.py: https://github.com/mikekistler/bin/blob/main/lint-summary.py
Here's an example of that, for PR #42565:
f=specification/purviewdatagovernance/data-plane/DataAccess/preview/2023-10-01-preview/DataAccessApiService.json
lint-summary.py $f
38 40.0% az-operation-id
OperationId for put should not contain "Update"
OperationId for get on a collection should contain "list"
OperationId for put without 201 response should not contain "create"
OperationId for put with 200 response should contain "replace"
26 27.4% az-path-case-convention
Static path segments should be kebab-case.
7 7.4% az-success-response-body
All success responses except 204 should define a response body.
7 7.4% az-pagination-response
Operation might be pageable. Consider adding the x-ms-pageable extension.
6 6.3% az-put-path
Put on a path that does not end with a path parameter is uncommon.
4 4.2% az-delete-response-codes
A delete operation should have a `204` response.
2 2.1% az-datetime-naming-convention
Use an "At" suffix in names of date-time values.
2 2.1% az-boolean-naming-convention
Do not use "is" prefix in names of boolean values
1 1.1% az-ms-enum-descriptions
Include descriptions for all values in the x-ms-enum extension.
1 1.1% az-security-definition-description
Security definition should have a description.
1 1.1% az-schema-names-convention
Schema name should be Pascal case.