Why PATCH is Good for Your HTTP API
Wednesday, 5 September 2012
A common problem for APIs is partial update; when the client wants to change just one part of a resource’s state. For example, imagine that you’ve got a JSON representation of your widget resource that looks like:
{
"name": "abc123",
"colour": "blue",
"count": 4
}
and you want to update the “count” member’s value to “5”.
Now, you could just PUT the entire thing back with the updated value, but that requires a recent GET of its state, can get heavyweight (especially for mobile clients), and requires use of pre conditional requests to avoid “lost updates.”
For these and other reasons, many APIs define a convention for POSTing to resources that allows partial updates. E.g.,
POST /widgets/abc123?action=incrementCount
It seems simple, at first. However, this is a POST, so it doesn’t have any generic semantics; the server and client side developers have to write application-specific code to support it, then do QA on it, debug the corner cases, and eventually rewrite the API to fix the problems they inevitably find (partial updates can get subtle). Once you get a lot of these hanging around, it’s a pain.
Some folks go down the path of writing a more generic query parameter conventions for talking about the structure of a document in an API; this can be seen in the CIMI specification’s “$select” mechanism, as well as XCAP:
Assume that this document has a document URI of
"http://xcap.example.com/test/users/sip:joe@example.com/index",
where "test" is the application usage. This application
usage defines a default document namespace of
"urn:test:default-namespace". The XCAP URI:
http://xcap.example.com/test/users/sip:joe@example.com/index/
~~/foo/a:bar/b:baz?xmlns(a=urn:test:namespace1-uri)
xmlns(b=urn:test:namespace1-uri)
will select the first <baz> child element of the
<bar> element in the document.
These approaches still bring a lot of complexity to the API; what used to be a fairly simple URI space is now made much deeper, more complex, and brittle. There’s a lot more application-specific code to write, document, debug, understand and use. Blech.
Enter PATCH
A better way is to use the HTTP PATCH method. Rather than putting the semantics into the URI, or into an application-specific POST, we put them into a generic* request format – you can think of this as a diff – which is activated by the PATCH method.
This has a bunch of advantages. Since it’s a generic format, you can write server- and client-side code once, and share it among a number of applications. You can do QA once, and make sure you get it right. Furthermore, your API will become less complex, because it has less URI conventions, leading to more flexibility and making it easier to approach for new developers. Using this approach will also make caches operate more correctly; since modifications to a resource will “travel” through its URL, rather than some other one, the right stored responses will get invalidated.
One final benefit; PATCH is atomic, which means that if you need to do some complex changes to the state of a resource, you can do it with confidence. It also means that you can create synthetic resources and PATCH them if you need to orchestrate changes to the state of several resources.
So, what does PATCH look like?
PATCH with JSON
We’ve been working on a PATCH format for JSON in the IETF APPSAWG. Originally proposed by Paul Bryan, I’ve been editing it so that we can get it out the door. A request to patch a JSON document looks like this:
PATCH /widgets/abc123 HTTP/1.1
Host: api.example.com
Content-Length: ...
Content-Type: application/json-patch
[
{"replace": "/count", "value": 5}
]
Easy to understand, and even write by hand. If it succeeds, the response can be as simple as:
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Your patch succeeded. Yay!
If you want to get fancy, you can also use James Snell’s Prefer mechanism to tell the server your preferences for the response, along with content negotiation for its format, of course.
PATCH with XML
The closest thing we have to json-patch for XML is RFC5261, but the media type it defines is application specific, and it’s somewhat complex to boot. I’m not aware of any Open Source implementations, so I’m not sure if it’d be best to start here, or just come up with something new. Thoughts? Or, just don’t use XML.
Supporting PATCH in Frameworks
JSON Patch is almost ready for Working Group Last Call, which means it’ll be an RFC sometime soon. I want to get some implementation experience with it before then (there are already a few people dipping their toes in), and I think both client-side and server-side frameworks can make PATCH even easier. For example, on the client side, it should be easy to “record” modifications to a JSON object and then serialise them as a JSON Patch document. On the server side, the persistence layer can do lots to make PATCH automatic, as well as enabling things like enforcing access rights, triggering side effects, etc. If you’re aware of any JSON Patch implementation, have ideas, or want to help (we need to build a test suite), please comment below, or contact me.
- Some people have misinterpreted the PATCH specification as saying that any media type (e.g., application/json) can be used as a PATCH format. This is missing the point; if you do that, you’re not getting any benefit from shared code, etc. and you might as well use POST.
30 Comments
Jirka Kosek said:
Wednesday, September 5 2012 at 10:05 AM
rektide de la fey said:
Thursday, September 6 2012 at 8:13 AM
rektide de la fey said:
Thursday, September 6 2012 at 10:11 AM
Mike Amundsen said:
Thursday, September 6 2012 at 12:42 PM
thomas.koch.ro said:
Thursday, September 6 2012 at 12:52 PM
Mark Nottingham said:
Saturday, September 8 2012 at 1:12 AM
Mark Nottingham said:
Saturday, September 8 2012 at 1:14 AM
Mark Nottingham said:
Saturday, September 8 2012 at 1:18 AM
Jon Moore said:
Saturday, September 8 2012 at 2:01 AM
markus-lanthaler.com said:
Saturday, September 8 2012 at 2:01 AM
Mark Nottingham said:
Saturday, September 8 2012 at 2:02 AM
Johan Sundström said:
Saturday, September 8 2012 at 3:09 AM
jaaju said:
Friday, September 14 2012 at 3:13 AM
Andrei Neculau said:
Sunday, September 23 2012 at 8:06 AM
Mark Nottingham said:
Sunday, September 23 2012 at 10:47 AM
Jon Moore said:
Tuesday, October 9 2012 at 11:15 AM
eugene-beresovksy.myopenid.com said:
Wednesday, October 10 2012 at 2:44 AM
Mark Nottingham said:
Wednesday, October 10 2012 at 3:22 AM
Mike Schinkel said:
Friday, December 14 2012 at 12:21 PM
Mark Nottingham said:
Monday, December 17 2012 at 2:04 AM
http://openid.elfisk.dk/jornwildt said:
Wednesday, January 2 2013 at 6:27 AM
Mark Nottingham said:
Friday, January 4 2013 at 9:15 AM
Tim Stokes said:
Friday, March 8 2013 at 1:13 AM
Mark Nottingham said:
Friday, March 8 2013 at 6:10 AM
Tim Stokes said:
Friday, March 8 2013 at 10:10 AM
Mark Nottingham said:
Monday, March 11 2013 at 11:41 AM
Tim Stokes said:
Tuesday, March 12 2013 at 3:58 AM
Joachim Wester said:
Sunday, March 17 2013 at 12:18 PM
https://me.yahoo.com/nickshanks#e0fbf said:
Thursday, August 8 2013 at 6:21 AM
Mark Nottingham said:
Thursday, August 8 2013 at 10:38 AM