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.stormpath.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.

Get Started with Stormpath

User Management API for Developers