Why PATCH is neither safe nor idempotent?
Asked Answered
S

4

67

I have trouble understanding why PATCH is not safe where PUT is. Aso the idempotent part - if I update a field of the resource, wouldn't that field return the same value after update?

Sinistral answered 30/12, 2016 at 5:18 Comment(3)
Related: softwareengineering.stackexchange.com/questions/260818/…Chromomere
PUT is not safe. OPTON, GET , HEAD are only safe.Fredrick
@Tony Vincent - Since the accepted answer has negative votes, wondering if you could change the accepted answer. This helps visitors to get the right info quickly.Clerissa
V
101

It's not safe because in general you can't safely execute a PATCH request without changing a resource (That's what it's for).

So why is PATCH not idempotent compared to PUT? It's because it matters how you apply your changes. If you'd like to change the name property of a resource, you might send something like {"name": "foo"} as a payload and that would indeed be idempotent since executing this request any number of times would yield the same result: The resources name attribute is now "foo".

But PATCH is much more general in how you can change a resource (check this definition on how to apply a JSON patch). It could also, for example, mean to move the resource and would look something like this: { "op": "move", "from": "/a/b/c", "path": "/a/b/d" }. This operation is obviously not idempotent since calling at a second time would result in an error.

So while most PATCH operations might be idempotent, there are some that aren't.

One remark on the other answers: Idempotence is defined by repeating an operation multiple times in a row. Saying that something is not idempotent because the effect is different if some other operation was executed in between or in parallel just isn't a valid argument (no operation would be idempotent in general if that was the case). Mathematically, an idempotent transformation is one that yields the same result, no matter how often you apply it (like rotating something 360 degrees). Of course two 360 deg rotation might yield different results if you apply any other operation in between.

Virgievirgil answered 20/7, 2018 at 11:24 Comment(2)
I think you fell in the same trap as the other answers that you criticise: sending a 2nd PATCH request with your JSON patch may not give you the same response, but the state of the resource would be the same as that after the 1st PATCH request, so in this case your PATCH request is idempotent. A PATCH request that is not idempotent would be for instance appending items to an array: using JSON Patch format, {"op": "add", "path": "/-", "value": "foo"} transforms [] to ["foo"] the 1st time, then to ["foo", "foo"] the 2nd time, then to ["foo", "foo", "foo"] the 3rd time, etc.Elva
This seems to be the most accurate explanation. So i guess as a convention it could be if we are pretty sure that operation is idempotent then either Patch or Put. If we are pretty sure update causes non idempotent then definitely patch, it can never be put. Now coming to formats like diffed updates or total updates , i would say its up-to API designer to adopt consistent convention and once adopted should not be changed. Generally for any type of idempotent updates i will use put, but once such updates becomes non idempotent then patch.Croatia
A
10

I recently started looking if Patch is idempotent or not and after reading about the JSON patch format, i understood that if the add operation is applied using the Patch method its completely possible that the request is non-idempotent as it could add the new values to an existing resource if the same request is made multiple times.

{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] }

Antifebrile answered 24/12, 2018 at 22:1 Comment(3)
What if we use add operation using PUT method? Wont that also continue to add multiple times?Nosey
Looks like the convention is to use PATCH if its not idempotent.Satellite
Why would the body have an add operation in it? That's not RESTful, it's RPC. An "add" operation is a POST to the /a/b/c resource.Swung
J
9

PATCH changes the resources attributes. The change may require a concrete previous value of the attribute, which makes it NON idempotent.

From Name=John to Name=Gargantua. 

After repeated applying the name will be Gargantua and patch will fail, since it requires the name to be "John" before the change

"from Name=John"

Joseph answered 14/2, 2018 at 8:10 Comment(1)
The conditional change you mention, is that conditional requests tools.ietf.org/html/rfc7232 or something else? Aren't those also possible for PUT?Chromomere
D
-4

First of all, PUT isn't safe either.

Safe methods are HTTP methods that do not modify resources. For instance, using GET or HEAD on a resource URL, should NEVER change the resource.

Since PUT request (so does PATCH for that matter) updates the resource, so it can't be cached and hence it's not SAFE.

PUT request is idempotent though, or I should say PUT request should be idempotent.

An idempotent HTTP method is a HTTP method that can be called many times without different outcomes. It would not matter if the method is called only once, or ten times over. The result should be the same. Again, this only applies to the result, not the resource itself. This still can be manipulated (like an update-timestamp, provided this information is not shared in the (current) resource representation.

The idea behind PUT request being idempotent is that if an update call on a resource fails, a client can make the same call again without causing any undesirable or inconsistent state. PUT request should always be preceded by a GET request to the resource, and should succeed if and if only resource hasn't changed since. For elaboration:- go through one of the similar answer - Idempotent PUT request in concurrent environment

Now PATCH request is intended to update just selective fields, it is not expected to GET the resource representation. So multiple calls to PATCH request could end up in undesirable change in the resource state. Hence it is not IDEMPOTENT.

For example:- There is resource Person

Request 1: PATCH /person/1 {'age': 10} - updates the age of resource to 10

Now suppose some other parallel request changes the state of resource, say

Request 2: PATCH /person/1 {'age': 19} - updates the age of resource to 19

Now if sends the Request 1 again it would updates the resource age to 10 again, hence causing an undesirable result.

It can be made idempotent though using etags or If Modified Since headers.

Diapason answered 30/12, 2016 at 6:14 Comment(6)
If the user sends request 1 again, then why is the result assumed undesirable? I could see the argument for PUT since you have to provide all properties just to change one, but for PATCH you limit the properties to just the ones you expect to be updated. (Also did you mean "updates the resource age to 10 again"?)Bonner
I have fixed the typo as you suggested, thanks. It's undesirable in the sense, that both the requests are trying to update the resource on a stale assumption. Let's say initial value of a resource is 7. Since that value is wrong, one request wants to update 7 to 10, other 7 to 11, not 10 to 11 or vice-versa.Diapason
Interesting topic for discussion. I could see that for a PUT where user 1 updates age & attempts to keep the name while user 2 updates name & attempts to keep the age. But for a PATCH they're explicitly trying to update the age. Seems like those two people just need to physically talk to each other to decide on the right value :) However, I have heard a general discussion for PATCH claiming its unsafe since some implementations require a preset state (i.e. tools.ietf.org/html/rfc6902 - JSON PATCH has some operations that are unsafe in this same way).Bonner
I wonder if example with patch could be like: (1) PATCH {"age":"10"} -> {"age":"10", "sex":"f","name":"a"} (2) PATCH {"name":"b"} -> {"age":"10", "sex":"f","name":"b"} (3) PATCH {"age":"10"} -> {"age":"10", "sex":"f","name":"b"} (1) and (3) Requests are the same but return different responses.Macintyre
This argumentation is wrong.The same applies to DELETE, which is idempotent. What is, if the resource was recreated inbetween? The important thing is - the result after DELETE and after your PATCH example, which just sets an attribute to a new value - will be the same. PATCH is not idempotent, because it may contain relative changes, which assume a concrete resource state, see my answer.Joseph
Bad logic: with your example PUT would not be idempotent either, since a PUT request in between could update the resource as well.Elva

© 2022 - 2024 — McMap. All rights reserved.