I am learning OpenAPI recently, and would like to know the best practices.
Let's say I have a resource called Person
, and it is defined in components/schemas
as follows:
Person:
type: object
required:
- id
- name
- age
properties:
id:
readOnly: true
type: integer
name:
type: string
age:
type: integer
I've made id
readOnly because when I do post
or patch
, the ID will be passed as part of the URL. See https://swagger.io/docs/specification/data-models/data-types/
name
and age
must present when the client tries to create a new person using post
method, or get
a person, therefore they are defined as required
.
My question is about patch
: what if I only want to update a person's age
or name
independently? Ideally I would like to do something like
PATCH /person/1
{"age": 40}
However, since I've defined name
as required, I can't do it. I can think of several solutions, but they all have flaws:
- Remove the
required
restriction. But if I do that, I lose the validation onpost
andget
. - Use a separate schema for
patch
, e.g.PersonUpdate
, withrequired
removed. Apparently that leads to redundancy. - When I do
patch
, I do pass all the fields, but for the ones I don't want to update, I pass an invalid value, e.g.
PATCH /Person/1
{"age": 40, "name": null}
And make all the fields nullable
, and let the server ignore these values. But what if I do want to set name to null
in DB?
- I use
PUT
for update, and always pass all the required fields. But what if my data is outdated? E.g. when I do
PUT /Person/1
{"age": 40, "name": "Old Name"}
Some other client has already changed name
to "New Name", and I am overriding it.
- Like method 3, but I pass additional fields when doing
patch
to indicate the fields the server should care, whether using query parameters like?fields=age
, or add it to the JSON body. So I can change therequestBody
to something like
requestBody:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Person'
- type: object
properties:
_fields:
type: array
items:
type: string
Then I can do this
PATCH /Person/1
{"age": 40, "name": null, _fields: ["age"]}
In this way, I can update name
to null
as well
PATCH /Person/1
{"age": 40, "name": null, _fields: ["age", "name"]}
This method seems can work, but is there a better or widely accepted practice?