Allow requiring fields only on input or only on output
See original GitHub issueSay we have an object like this:
person:
required:
- name
properties:
name:
type: string
These objects are sent to the API via POSTs and read via GETs. We have many many client devices out there all reading and writing these objects, so when we put out a breaking change in a new version of the API, we have to support the old ones, at least temporarily. Now we add a new field, say age
. We want this to be a required property, so we need a new API version. At /v1 we’ll have the old API and at /v2 we’ll have the new one. But now there’s a problem. Say a v1 client POSTs a person
object. Now a v2 client attempts to fetch this data with a GET. The age
data isn’t available. The API cannot construct a person
object that fits the v2 spec. So do we make the age
field optional in the v2 spec? No, because then we’re not validating client input. Our v2 client code might be failing to include the age
property in some cases, and it’ll just fail silently, and we might not catch the bug before the code goes out into production.
The correct solution, as I see it, is for age
to be required on input, but not on output. v2 clients should always include this new information, but since the API itself is having to relay information from v1 clients, it simply can’t always do that. My ideal syntax would be this (in the v2 spec):
person:
required:
always:
- name
write:
- age
properties:
name:
type: string
age:
type: number
There would similarly be a required.read
property. If it’s not possible to implement this syntax in a backwards-compatible way, maybe something like required
, requiredOnRead
, requiredOnWrite
.
(Warning: rambling ahead. Optional reading)
Just to provide further rationale: to me this isn’t a niche use-case, I think this is basically the tool which solves all of your API versioning woes. When we’re dealing with symmetrical APIs (clients read and write more or less the same type of data), basically when you break an API, you do it in one of three ways:
- You add a field (new clients will be sending more data).
- You remove a field (new clients will be sending less data).
- You change the data type of a field (new clients will be sending different data).
The way you handle these changes is in each case to create a new v2 spec and put the APIs on /v1 and /v2 (at least this is one way of handling it), in which case we can handle them in these ways:
- New clients are sending and expecting the
age
field, but old ones aren’t. The problems that can arise and the way to handle this with input-only required properties is detailed above. - New clients are no longer sending the
age
field, but old clients expect it. This is going to break the solution no matter what. Old clients making a GET request to /person?id=12 are expecting to get an age, but the information isn’t available. Assuming age was a required property in the v1 spec (if it isn’t there’s no problem), there’s just no way around it: something’s going to break. No clever new OpenAPI feature can fix this, so we can forget about this case. - Field F has changed from data type A to data type B. If it is not possible to translate type B into type A in a reasonable fashion (like if maybe the fields only got renamed) then we’re in case (2), above: information that old clients want is no longer available from new clients, we can’t do anything about it, and we forget this case. If it is not possible to translate type A into type B in a reasonable fashion, this means new clients are looking for information which old clients aren’t providing. This is the same as case (1), above: the solution is to make the field optional on output and required on input.
To be honest, I haven’t yet thought of a use case requiring making a field required on input but not on output, but for the same of symmetry it should probably be included.
Issue Analytics
- State:
- Created 5 years ago
- Reactions:1
- Comments:6 (4 by maintainers)
Top GitHub Comments
@geajack, I think this is one of many cases where you’d need to define separate request and response schemas for each version, in order to fully define the data contract as you’ve described it.
Summarizing the combinations described in the Schema Object specification:
You’re looking for options that would make a property:
OpenAPI doesn’t provide a way to specify those semantics within a single schema.
It’s been more than six months since the last answer with no further discussions so I’m going to close this. Please re-open if needed.