Create, Update and HTTP Idempotence
For developers building REST-based APIs, there is a great deal of misinformation and some understandable confusion about when to use HTTP PUT and when to use HTTP POST. Some say, POST should be used to create a resource, and PUT to modify one. Others that PUT should be used to create, and POST to modify one. Neither is quite right.
Often, developers think of each HTTP method as a 1:1 relationship with CRUD operations:
CRUD HTTP
Create POST
Read GET
Update PUT
Delete DELETE
This can be true, with GET and DELETE specifically, but when it comes to which HTTP methods should be associated with create and update, the answer comes down to idempotency.
Idempotency
Idempotence is an important concept in the HTTP specification that states idemptotent HTTP requests will result in the same state on the server no matter how many times that same request is executed. GET
, HEAD
, PUT
, and DELETE
all have this attribute, but POST
does not.
To help illustrate idempotency, we will use an Account collection (“/accounts
) and for brevity we will say that each account resource has three properties called givenName
, surname
, and status
.
Let’s say you submit an update request using the HTTP PUT method. In the body, you set givenName
a value of “John†and surname
a value of “Smithâ€. Later, you submit another request HTTP PUT request, this time setting givenName
to “Johnnyâ€. Is this idempotent? No. Why? Because other requests might have changed the server state of the account resource in between our two requests. For instance, between the two requests, status
could have been changed to “blocked.†Our example request cannot guarantee that the state of the account on the server is identical when it is repeated.
Request:
HTTP/1.1 PUT /accounts/abcdef1234
{
“givenNameâ€: “Johnâ€,
“surnameâ€: “Smithâ€
}
Possible account state after our two requests (due to side effects from other requests):
{
“givenNameâ€: “Johnâ€,
“surnameâ€: “Smithâ€,
“statusâ€: “enabledâ€
}
or
{
“givenNameâ€: “Johnâ€,
“surnameâ€: “Smithâ€,
"statusâ€: “disabledâ€
}
To quote Dino Chiesa, “PUT
implies putting a resource – completely replacing whatever is available at the given URL with a different thing.†With PUT
requests you MUST send all the available properties/values, not just the ones you want to change. If we were to send the status of “disabled†in addition to the givenName
and surname
, the call would be idempotent and eliminate the side effects. Idempotency is a fundamental property of the HTTP specification and must be adhered to to guarantee web interoperability and scale.
Finally, we should point out that HTTP idempotency only applies to server state – not client state. For example, a client could send a server-idempotent request successfully, and then send that same exact server-idempotent request immediately again and experience an error (e.g. perhaps due to a constraint violation in the server) and this is totally ‘legal’ for HTTP. As long as the requests result in the same identical state on the server, HTTP idempotency is maintained.
HTTP POST vs HTTP PUT
Now that idempotency is clear, which method should you use when performing create and update operations? The following is a quick reference for when it is appropriate to use each method.
Creates
Use POST to create resources when you do not know the resource identifier. With POST creates, it is best practice to return the status of “201 Created†and the location of the newly created resource, since its location was unknown at the time of submission. This allows the client to access the new resource later if they need to.
HTTP/1.1 POST /accounts
{
…
}
Response:
201 Created
Location: https://api.puresourcecode.com/accounts/abcdef1234
Use PUT when you allow the client to specify the resource identifier of the newly created resource. But remember, since PUT is idempotent, you must send all possible values.
HTTP/1.1 PUT /accounts/abcdef1234
{
“givenNameâ€: “Johnâ€,
“surnameâ€: “Smithâ€,
“statusâ€: “enabledâ€
}
Updates
You can use POST
to send either all available values or just a subset of available values:
HTTP/1.1 POST /accounts/abcdef1234
{
“statusâ€: “disabledâ€
}
Response 200 OK
If you want to use PUT
to update a resource, it must be a full resource update; you MUST send all attribute values in a PUT
request to guarantee idempotency.
Use PUT
when you want or need to send all available values in order to follow idempotency requirements, for instance in the case of a full resource update. I
HTTP/1.1 PUT /accounts/abcdef1234
{
//FULL RESOURCE UPDATE
“givenNameâ€: “Jâ€,
“surnameâ€: “Smithâ€,
“statusâ€: “Enabledâ€
}
You can also use POST
to send all values as well and the server state could be the same as a full PUT
– it’s just not required to be by the HTTP specification. Note that idempotency has a strong correlation to being cacheable by HTTP caching servers, and therefore POST
requests are generally not cached. If you are ok with this caching side effect, you can use POST
for both full and partial updates.
POST
is currently the only non-idempotent method. The HTTP specification is very generic about it, and basically states it to be a ‘server processing directive’. This means it is ‘safe’ to do whatever you want during a POST
request.
Finally, we should note that there is another method that has not been finalized for the HTTP specification yet, called PATCH
. PATCH
is meant to be used as an alternative to POST
for partial updates. However, since POST
can already simply handle partial updates, there doesn’t seem to be a strong movement for PATCH
to be finalized and approved quickly by the HTTP specification committee. If approved, PATCH
would join POST
as the only other non-idempotent HTTP method.