DRY Specifications via $ref
See original GitHub issueSo I realize there is some discussions regarding some “similar” things, but I’m not quite sure they’re similar enough - if so, please say so and I’ll contribute to the other discussions accordingly and close this one out. Other similar but not quite as extensive discussions I have found:
- https://github.com/OAI/OpenAPI-Specification/issues/556 : polymorphism around $ref
- https://github.com/OAI/OpenAPI-Specification/issues/417 : paths and $ref
Basically, one should be able to write their OpenAPI specification in as DRY a manner as possible. Presently this is not possible because:
$ref
is not valid at all the right placesallOf
is not valid at the right places (which could make up for point 1)$ref
can’t be specified numerous times next to itself appropriately
Let’s give this some better clarity via an example. Discussing API Header Fields provides a good example such as the following which similar to #417 but I’m going more general where that seems to be focused on path objects:
paths:
/:
get:
parameters:
- $ref: `#/components/headers/x-my-header-1`
- $ref: `#/components/headers/x-my-header-3`
responses:
'401':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'403':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'200':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-2`
$ref: `#/components/headers/x-my-header-3`
'301':
headers:
allOf:
- $ref: `#/components/headers/x-my-header-1`
- $ref: `#/components/headers/x-my-header-2`
- $ref: `#/components/headers/x-my-header-3`
default:
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
post:
parameters:
- $ref: `#/components/headers/x-my-header-1`
- $ref: `#/components/headers/x-my-header-2`
responses:
'401':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'403':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'200':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-2`
$ref: `#/components/headers/x-my-header-3`
default:
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
/howdy:
delete:
parameters:
- $ref: `#/components/headers/x-my-header-1`
- $ref: `#/components/headers/x-my-header-2`
responses:
'401':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'403':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
'200':
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-2`
$ref: `#/components/headers/x-my-header-3`
default:
headers:
$ref: `#/components/headers/x-my-header-1`
$ref: `#/components/headers/x-my-header-3`
components:
headers:
x-my-header-1:
required: true
description: my first header
schema:
type: string
format: uuid
x-my-header-2:
required: true
description: my second header
schema:
type: string
x-my-header-3:
required: true
description: my third header
schema:
type: integer
The above is missing a lot of stuff, but only in order to show the re-use and avoid having to put a complete spec here. I know some of the above can be fixed by moving up a layer in the referencing, e.g create a component for the header set and reference that instead; however that can then dictate a bad spec by forcing all headers to be pushed to header sets which then can also be an issue for 1-off header combinations.
Goal here is to enable spec writers to manage their specifications by creating re-usable modules that can be continuously re-used. To achieve this, $ref
would need to be valid in essentially ever object to replace anything in the object. F.e if you have a description
field you want to re-use, define it once and use it 10 times.
Two parallel $ref
values should be valid and able to point to separate objects, neither being discarded, or allOf
could be used to combine the contents of both $ref
references to create the same effect (good solution for one-offs).
When building larger APIs using tooling like OpenAPI being able to be a DRY as possible is key to keeping bugs from creeping into the specs as it reduces the ability for any one instance to be mis-typed. For instance, if you had to type x-my-header-3
for every single Request object and x-my-header-2
for every single Response object - one typo of my-header-3
or xmy-header-3
could easily create something hard to detect where using the $ref objects would make it fail validation and be easily caught in gate checks (PR builders, etc).
To re-iterate - please let me know if I need to file this with JSON Reference/Schema too. I did not find anything suitably talking about these aspects while reading through any of the repos I came across, but have in general there does seem to be some of sentiment that this is an issue.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:2
- Comments:12 (7 by maintainers)
Top GitHub Comments
@MikeRalphson OK the reference object rules in OAS match how JSON Schema works. In older drafts it was outside of the spec and therefore could be used anywhere, but we changed that in draft-05. We have strong reasons for not allowing $refs to things other than schemas but it’s also a long story so I’ll leave it at that 😃
@BenjamenMeyer I’m one of the editors of the JSON Schema spec.
There are a couple of things going on here, some due to JSON and YAML, and others specific to OAS. As phrased, none of this is actually about JSON Schema.
For all practical purposes, JSON does not allow allow duplicate keys in objects. They do not technically violate the RFC, but the RFC notes that the behavior is undefined and therefore not interoperable. Furthermore, YAML absolutely requires unique keys. So your number 3, putting multiple
$ref
keys in the same object, is not possible.The correct way to have multiple
$ref
s is to simply wrap them in anallOf
:which reduces your 3 to a combination of 1 and 2. Those are limitations of OAS. JSON Schema allows
$ref
anywhere a schema is expected, and allowsallOf
as a keyword in any schema object. There are a number of issues and discussions going on about whether and how to converge OAS’s schema variant and JSON Schema proper, although there is no particular timeline on that right now.