I’m seeking input from people if you have an opinion on the technical method we use to mark HSDS API endpoints as REQUIREd in the OpenAPI.json file. This is to a) support future validation tooling producing a compliance report and b) bringing the OpenAPI file in line with the current specification.
Currently, the only REQUIRED endpoints in the specification are:
/
/services
/services/{id}
The contradiction I’m trying to resolve is that OpenAPI is really designed for describing a single instance of an API, rather than prescribing how an API should conform to a specification. Therefore it lacks features such as marking endpoints as required.
This has the effect that anyone trying to implement API validation needs to maintain a separate list of the endpoints required by the HSDS Specification, in order to decide whether the overall API is conformant or not i.e. “your endpoints are all producing valid data, but you lack the /services/{id} endpoint so sadly you’re not compliant with the specification”
I noticed that the CommonGrants folk got around this by using OpenAPI’s Tag object, and tagged particular endpoints with required.
An alternative way to do this is to extend OpenAPI’s specification with a custom field which would mark the endpoint as required. This is supported by OpenAPI’s specification:
Both approaches are similar in terms of pragmatics. We’d either create a required tag in our OpenAPI specification docs and apply it to endpoints, or we’d create a x-required: true property and apply it to endpoints.
I ran this by our old friend Kin Lane, API Evangelist and OpenAPI expert. He says:
“Your three options are 1) Tags, 2) Extensions, 3) Separate OpenAPIs for required or additional paths / operations and being explicit in the description.”
Great to have this input and glad to see us on the right track already. I hadn’t considered splitting the required and non-required endpoints into separate openapi.json definitions. There is an elegance in that simplicity; “everything defined in this file is required, everything in the other file is optional”.
My only hesitation with that option is for Profiles, where I think HSDS Schema tools used to generate the Profiles assume a single openapi file. But maybe the tools are actually flexible enough to handle splitting it into two; they actually just match based on names and calculate the diffs, so it’s reasonable that this could work out of the box.
This leaves us with three options now! Keen to hear others’ thoughts on this, as once we have something of a direction I can spin up a proposal doc to get in front of the committee.
Thanks for flagging this thread for me! As we talked about a few weeks ago, CommonGrants grappled with a similar question regarding our OpenAPI spec. As @bloom mentioned the three main options we considered were: 1) Tags, 2) Extensions, 3) Separate OpenAPI docs.
We ultimately chose tags, because it:
Worked well with existing OpenAPI tooling without customizations (e.g. Swagger docs, swagger-parser)
Allowed us to support multiple statuses (e.g. required, optional, experimental)
Allowed us to maintain a single OpenAPI spec per version
Allowed adopters to reference a single spec for implementation and validation
The tool-compatibility and single OpenAPI spec were strong motivators because we have a CLI tool that checks APIs against our spec that would be a lot more complicated to implement if we had either a custom extension or multiple specs per version.
This is good motivation to add this decision to our ADRs in the next round of website updates as well. Here’s the draft ADR for that decision below:
Decision
As described in our specification, we chose to use tags to categorize API routes into three main categories:
Required: Stable endpoints that adopters MUST implement
Optional: Stable endpoints that adopters MAY implement
Experimental: Non-stable endpoints that adopters MAY implement to provide feedback
Positive consequences
Allows us to use the standard OpenAPI rendering tools like Swagger, RapiDoc, etc. without any customizations
Allows us to use standard OpenAPI parsers like swagger-parser without any customizations
Allows us to validate API implementations against a single spec, instead of multiple
Only requires maintaining or updating a single spec per API version
Enables OpenAPI spec consumers to understand the meaning of required status from the OpenAPI spec itself
Negative consequences
Causes routes to be listed twice (may be a feature or a bug depending on how you look at it)
Overloads the use of tags in the OpenAPI spec
Criteria
Machine-readable: Clearly identifies route status in a machine-readable way.
Easy-to-render: Clearly identifies route status in the human-readable rendering of OpenAPI spec (e.g. via Swagger docs)
Tool compatible: Recognized by and compatible with most standard OpenAPI tooling (e.g. swagger-parser)
Maintainable: Easy to maintain and version alongside other API changes over time.
Adopter DX: Easy for adopters to reference a single spec when consuming, implementing, or validating against the protocol.
Self documenting: Clearly documents how to interpret required status in the OpenAPI spec itself.
Scalable: Easily supports additional statuses in the future, if needed.
Options considered
Tags
Extensions
Separate specs
Evaluation
Side-by-side comparison
Criteria
Tags
Extensions
Separate specs
Machine-readable
Easy-to-render
Tool compatible
Maintainable
Adopter DX
Self documenting
Scalable
Tags
Use the standard tags field in the OpenAPI spec to categorize routes into three main categories:
required (e.g. tags: [required])
optional (e.g. tags: [optional])
experimental (e.g. tags: [experimental])
Each tag would have a description that explains the meaning of the tag.
Bottom line: This is the best option if:
We want to identify route status in a self-documenting way that is compatible with standard OpenAPI rendering and parsing tools,
But we’re okay with overloading the use of a standard OpenAPI spec feature.
Pros
Supported by standard OpenAPI rendering tools like Swagger, RapiDoc, etc. (e.g. Swagger UI will render tags as a collapsible section)
Supported by standard OpenAPI parsers like swagger-parser (e.g. swagger-parser will parse tags as a list of strings)
Enables us to validate API implementations against a single spec, instead of multiple
Only requires maintaining or updating a single spec per API version
Self-documenting - Descriptions of tags can be used to explain the meaning of required status
Scales well to support additional statuses in the future, if needed
Cons
Causes routes to be listed twice (may be a feature or a bug depending on how you look at it)
Overloads the use of tags in the OpenAPI spec
Each tag section might get cluttered if we have a lot of routes
Extensions
Create a new extension field x-status in the OpenAPI spec to categorize routes into three main categories:
required (e.g. x-status: required)
optional (e.g. x-status: optional)
experimental (e.g. x-status: experimental)
Each status would have a description that explains the meaning of the status.
Bottom line: This is the best option if:
We want to identify route status in a machine-readable way that avoids overloading a standard OpenAPI spec feature,
But we can compromise on direct compatibility with standard OpenAPI tooling or are willing to add custom tooling to support it.
Pros
Allows us to validate API implementations against a single spec, instead of multiple
Only requires maintaining or updating a single spec per API version
Scales well to support additional statuses in the future, if needed
Avoids overloading the use of tags in the OpenAPI spec
Cons
Not easily supported by standard OpenAPI rendering tools like Swagger, RapiDoc, etc.
Not easily supported by standard OpenAPI parsers like swagger-parser
Not as self-documenting as tags or separate specs, unless we were to add a description attribute to the extension field
Separate specs
Create a separate spec for each status.
Bottom line: This is the best option if:
We want to clearly separate routes by status while still being able to use standard OpenAPI tooling without overloading standard OpenAPI spec features,
But we’re willing to accept the overhead of maintaining multiple specs per API version asking adopters to reference multiple specs for implementation or validation.
Pros
Supports all standard OpenAPI tooling without customizations (e.g. each spec can be rendered independently)
Avoids overloading the use of tags in the OpenAPI spec
Self-documenting - Each spec’s top-level description can be used to explain the meaning of required status
Cons
Requires maintaining multiple specs per API version
Requires adopters to reference multiple specs for during implementation or validation
Becomes exponentially more complex to maintain as the number of statuses increases
Hey @billy.daly, thanks so much for taking the time out of your day to contribute to this thread! That means a lot; especially considering the depth of your response!
I think you’ve convinced me that Tags are in fact the best option for us now. In particular the support for multiple statuses, although I appreciate you outlining how we could frame an extension to support this as well, and thus avoiding the Boolean trap!
The sell of the Tags approach to work with tooling without modifications is something that I think is the most important at the moment. Our docs uses a sphinx plugin which gives us quite tight control over rendering individual routes, but I know quite a lot of our community like Swagger pages and it would be good to have the Tags supported here too.
My only concern would be overloading the use of Tags in OpenAPI, which you highlight. However, our spec doesn’t currently use Tags at all so I think this won’t be an issue for us in the near-to-medium term. If it ever become an issue, we can make plans for changing the approach in a future MAJOR version to make room for other use of Tags.
So the bottom line at the moment: I think we should be using Tags for the reasons you outline and I see benefit in outlining our Tags to yours. In practice I think we’d only use the required and optional Tags, but it’s good to know that experimental is there and already in use in another spec, should we ever see the need for it.
Unless I hear from others in this thread — or on Github — that they have concerns with the use of Tags as opposed Extensions or separate OpenAPI files, I’ll write this up as a proposal for the next version for the HSDS Specification and check you’re ok being added as an author.