skwee357 skwee357 - 3 months ago 27
reST (reStructuredText) Question

REST API - PUT vs PATCH with real life examples

First of all, some definitions:

PUT is defined in Section 9.6 RFC 2612:


The PUT method requests that the enclosed entity be stored under the supplied Request-URI. If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a modified version of the one residing on the origin server. If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new resource by the requesting user agent, the origin server can create the resource with that URI.


PATCH is defined in RFC 5789:


The PATCH method requests that a set of changes described in the
request entity be applied to the resource identified by the Request-
URI.


Also according to RFC 2612 Section 9.1.2 PUT is Idempotent while PATCH is not.

Now lets take a look at a real example. When I do POST to
/users
with the data
{username: 'skwee357', email: 'skwee357@domain.com'}
and the server is capable of creating a resource, it will respond with 201 and resource location (lets assume
/users/1
) and any next call to GET
/users/1
will return
{id: 1, username: 'skwee357', email: 'skwee357@domain.com'}
.

Now lets say I want to modify my email. Email modification is considered "a set of changes" and therefor I should PATCH
/users/1
with "patch document". In my case it would be a json
{email: 'skwee357@newdomain.com'}
. The server then returns 200 (assuming permission are ok). This brings me to first question:


  • PATCH is NOT idempotent. It said so in RFC 2612 and RFC 5789. However if I'll issue the same PATCH request (with my new email) Ill get the same resource state (with my email being modified to the requested value). Why isn't PATCH then idempotent?



PATCH is a relatively new verb (RFC introduced in March 2010), and it comes to solve the problem of "patching" or modifying a set of fields. Before PATCH was introduced, everybody used PUT to update resource. But after PATCH was introduced, it leaves me confused what is PUT used for then? And this brings me to second (and the main) question:


  • Whats the real difference between PUT and PATCH? I've read somewhere the PUT might be used to replace entire entity under specific resource, so one should send the full entity (instead of set of attributes as with PATCH). What is the real practical usage for such case? When would you like to replace / overwrite an entity under specific resource URI and why such operation is not considered as updating / patching the entity? The only practical use case I see for PUT is issuing a PUT on collection, i.e.
    /users
    to replace the entire collection. Issuing PUT on a specific entity makes no sense after PATCH was introduced. Am I wrong?


Answer

The reason PATCH is not idempotent is that it only handles partial modifications. Let's use your example PATCH request, and go through some operations.

PATCH /users/1
{"email": "skwee357@newdomain.com"}

Let's say that at some past time, a user was added. This is the state that you are starting from.

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@olddomain.com",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

After your PATCH, you have a modified resource:

PATCH /users/1
{"email": "skwee357@newdomain.com"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",    // the email changed, yay!
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "10001"
}

You know that this is the end state because when you sent your PATCH, the server replied with the new state.

If you then repeatedly apply your PATCH, you will continue to get the same state back in the response. A goes in, and B always comes out! So as you can see, it is idempotent.

Right?

An hour later, after you have gone to make some coffee and take a rest, someone else comes along with their own PATCH. It seems the Post Office has been making some changes.

PATCH /users/1
{"zip": "12345"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",  // still the new email you set
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"                      // and this change as well
}

The next day, you decide to send your PATCH again. It's ok, of course, because you are going to get the same state back that you got yesterday (because idempotence).

PATCH /users/1
{"email": "skwee357@newdomain.com"}

{
  "id": 1,
  "name": "Sam Kwee",
  "email": "skwee357@newdomain.com",
  "address": "123 Mockingbird Lane",
  "city": "New York",
  "state": "NY",
  "zip": "12345"
}

You got back a response with the email address that you updated yesterday; that's good. However, you also got this new zip code. That's not what you saw before. This may in reality be perfectly fine - you may not care that it's changed, depending on what you're doing.

However, it means that you can't call PATCH idempotent.

PUT is idempotent because you are sending the entire resource. That may mean that you're overwriting someone else's changes, of course. But it is idempotent: whenever A goes in, B always comes out.