RFC: Extension Authoring Best Practices (Adhering to OpenAPI Conventions/Standards) #4391
Replies: 2 comments 1 reply
-
I would love for this discussion to help us shape some advice, best practices and/or rules that extensions should consider to be compliant/consistent with OpenAPI. It would be really cool if some of this (the unwritten conventions/rules, ...) to end up somewhere in the specification or something we can point extension authors to. It would also be cool to get some opinions from the @OAI/tsc as well. |
Beta Was this translation helpful? Give feedback.
-
I don't actually mind any of these for extensions although 3 is pretty messy. But if there are examples documented somewhere then I think there's an argument that each of these might fit one use case or another. Only slightly related to option 5, the OpenAPI 3.2 version has a kubernetes-like syntax in its new |
Beta Was this translation helpful? Give feedback.
-
The purpose of this discussion is to talk about best practices as they relate to authoring extensions. While the OpenAPI itself doesn't voice an opinion as it relates to extension authoring best practices, I personally think that extension authors should take into account the conventions and "style" that the OpenAPI specification itself adheres to. The goal would be a consistent authoring and consumption experience for OpenAPI documents, including extensions.
Conventions
Below are a list of conventions (not exhaustive) that are not documented within the OpenAPI, but that happen to exist and could/should have an impact on OpenAPI extension authors.
Components Are for Reuse
The OpenAPI Specification has a Components Object which is used to contain all definitions that are to be reused (referenced by name or other supported mechanism) elsewhere throughout the OpenAPI document. Fortunately for extension authors, the Components Object supports extensions. This being said, any time an extension author needs to define objects that will be referenced from elsewhere within the OpenAPI document, defining an extension within the Components Object is the preferred way of doing this.
Based on the way that OpenAPI defines reusable definitions, each "type" has a container within the Components Object and each type-specific definition is within the container. So all Schema Objects are defined within
#/components/schemas
, and Security Definitions are defined within#/components/securityDefinitions
, etc. There is no unnecessary nesting, and each type-specific container contains only definitions of that type (you cannot define a "Schema Object" alongside a "Security Definition").Location Dictates Type
When authoring or consuming an OpenAPI document, reasoning about an object definition is as simple as knowing where in the OpenAPI document the typed object is defined. (For example, every definition within
#/components/parameters
is a "Parameter Object", and every definition within#/paths/{pathPattern}/parameters
is a "Parameter Object".) That being said, nowhere in OpenAPI are there locations where different types can be defined alongside each other. (For example, OpenAPI does not have an array/map of "Operation Objects" having atype
property that dictates the HTTP method for that operation, nor can you defined a "Parameter Object" and a "Schema Object" in the same hierarchical location.)Note: Security Definitions do have a
type
property that dictates which type of Security Definition, but this is more of a sub-type and less about different types themselves being defined alongside each other.Use Case
Let's use a real-world example where we want to define a quota limit as part of API Management for the API being described, and then applying that quota to an operation. This example defines a metric (read requests), a quota limit (referencing a metric) and applies a quota to a specific Operation Object (referencing a metric). The reusable type in this case is the metric, while the limit and quota application are not reusable themselves.
Options to Discuss
Below are a few options (not exhaustive, and there are likely permutations of these that could be discussed as well) to discuss.
Option 1 (Fully Compliant, Uses Components)
This option fully adheres to the OpenAPI conventions mentioned above. Below is the sample OpenAPI:
Option 2 (Fully Compliant, No Components)
This option fully adheres to the OpenAPI conventions mentioned above, but instead of using the Components Object for the reusable types (metrics), they are now isolated within a standalone extension (
x-acme-api
). Below is the sample OpenAPI:Option 3 (Noncompliant, Uses Components for Non-Reusable Types)
This option uses the Components Object for types that are not reusable. Below is the sample OpenAPI:
Option 4 (Noncompliant, Location Does Not Dictate Type, Security Definitions Approach)
This option is like Option 2, but it uses the same approach used for Security Definitions where the "type" is dictated by a
type
property and the other adjacent properties are mixed and type-specific. Below is the sample OpenAPI:Option 5 (Noncompliant, Location Does Not Dictate Type, Kubernetes Approach)
This option is like Option 2, but it uses the Objects in Kubernetes approach where you have a common wrapper definition and type is dictated by a combination of
kind
andspec
instead of location. Below is the sample OpenAPI:Beta Was this translation helpful? Give feedback.
All reactions