When creating an API I will usually plan to implement 4 of the HTTP verbs; namely GET
, POST
, DELETE
and PUT
. These four request methods are all that are needed to implement a CRUD (Create, Read, Update and Delete) interface. It is also common to use the GET
twice to implement a search interface, extending the acronym to SCRUD (or CRUDS).
There is another common HTTP verb that has not been mentioned above though and that happens to be the subject of this post; I of course refer to PATCH
and not HEAD
, CONNECT
, OPTIONS
or TRACE
which are also not mentioned but are a little more on the obscure side.
Recently I decided to implement a PATCH
method onto an API I was working on as it seemed appropriate. The context around this was to allow a boolean field to be updated and the original state was irrelevant to the client. One such use case would be notifications and marking them as read/acknowledged. With a newfound interest in PATCH
I began researching the standards governing it and was surprised to find that there are in fact two main types of PATCH
functionality which I will explore in this post; one being a Merge Patch and the other a JSON Patch.
Let's first assume no knowledge of the PATCH
request method, and for that matter, we can also assume no knowledge of PUT
. Both of these methods allow for a client to instruct the server to update a specific resource. The PUT
verb uses a replacement method whereas the PATCH
verb allows for more surgical alterations. Imagine a simple API that provides methods to interact with a user object and there is a user available at the following location: /api/user/1
.
The current state of this user is as follows:
{
"id": 1,
"forename": "John",
"surname": "Doe",
"email": "[email protected]"
}
How a PUT request works
The PUT
verb allows us to replace the entire state of the resource with what is being provided in the request body. For example, if we wanted to alter the email of the user above we could do the following request: PUT: /api/user/1
.
{
"forename": "John",
"surname": "Doe",
"email": "[email protected]"
}
On the face of it, this has done exactly what we wanted, the result is that the email has been updated. The downside of this method is that we need to provide the entire state of the object again meaning not only that our request is bigger than it needs to be but also that we need to know the current state of the resource before updating it.
How a PATCH request works
The PATCH
verb allows us to selectively replace a section of the state in a more surgical way than that provided by the PUT
method instead allowing us to alter (or add) individual keys in the JSON document.
As mentioned in the preamble at the beginning of this post, there are two types of PATCH
implementation. Let's take a look at both:
The 'Merge Patch' implementation
This implementation is perhaps the most common one and is the one that I have favoured when implementing a PATCH
method. The merge patch works by only acting upon keys that are present in the request. An important thing to note here is that this implementation of the PATCH
verb does not provide a mechanism to treat a null
value separately than a remove operation i.e. if knowing that a value was explicitly set to null is important then this implementation will not allow for it. If we wanted to update the email of the user (just like we did in the PUT
example) then we would execute the following request: PATCH: /api/user/1
.
{
"email": "[email protected]"
}
In this case, we only provided the field that we wished to update; there was no need to provide the full state. The spec for the merge patch can be found: here (RFC 7396).
The 'JSON Patch' implementation
This implementation uses a format called JSON Patch
which is a way to describe updates to a JSON document. This format defines a JSON schema that provides for six 'operations'; Add
, Remove
, Replace
, Copy
, Move
and Test
. If we wanted to use the JSON Patch
format to perform our user update then we would execute the following request: PATCH: /api/user/1
.
[
{
"op": "replace",
"path": "email",
"value": "[email protected]"
}
]
Again we did not need to provide the full state of the object and instead provided instructions on how to update the original document with our desired change.
Operations are passed inside of a JSON array allowing for multiple operations to be provided at once. There are a few benefits to this implementation of the PATCH
verb. For one it is more explicit by providing the desired operation, it allows for differentiation between the value of null
and the removal of a field if this is desired. The spec for the JSON patch can be found: here (RFC 6902).
How does the server know the difference...
... and what if I want to accept both types of patch?
The Content-Type
header can be used to instruct the server which type of patch you are using. Providing application/json
as the value can be used to denote a Merge Patch and providing application/json-patch+json
should denote a JSON Patch
.
Another header Accept-Patch
can be used by the server to advertise which types of patch are supported by returning the values mentioned above. See more about this header here.
Summary
After putting in the time to understand and appreciate the PATCH
request method I have come around to the thinking that having it is perhaps more useful than having a PUT
endpoint in many ways. But as the title "To PATCH or not to PATCH; that is not the question" suggests, whether or not we decide to implement PATCH
is not the real question. It is how we might go about it.
In this post, I explored two different methods of implementing a PATCH
endpoint. I currently prefer the 'Merge Patch' method but do find the 'JSON Patch' implementation interesting and worth consideration.
- An interesting StackOverflow question and set of answers exploring
PATCH
endpoints in detail. The answers also explore the idempotency of thePATCH
request method which is a very interesting read. This can be found here. - A great article on the differences between
PUT
and the two implementations of thePATCH
endpoint. This can be found here. - RFC 6902 (JSON Patch). This can be found here
- RFC 7396 (Merge Patch). This can be found here
Please feel free to share your thoughts and questions about this post!