To PUT or to POST when doing REST

When designing a REST API for your shiny new server, one of the first things you’ll encounter is the PUT vs. POST decision. Precisely, when you’re thinking about how to tell your server to create or update a resource, you’ll be deciding whether to use:

  • PUT for both
  • POST for both
  • PUT for creation, POST for update
  • POST for creation, PUT for update

At least, when first starting out with REST, I faced the challenge. If you’re a master of RESTful design and principles, then this isn’t for you.

The above gets especially pertinent if you’re framing your design in response to the popular CRUD operations – which is to Create, Read, Update and Delete. It seems perfectly convenient to map the CRUD operations to the REST “operations”. But I propose to you that that is force-fitting two design principles which are not equivalent.

One major key to understanding REST is to understand the concept of idempotent operations, or idempotency.

Idempotency

Wikipedia defines (with some simplification from me) idempotency as:

The property of operations that can be applied multiple times without changing the result beyond the initial application.

In other words, an idempotent operation is one that you can apply (assuming successfully) once, or a million times, and the state that the operation is manipulating is exactly the same after the first, or millionth, operation.

Here’s a concrete example.

Bagels

You have a database of bagel inventory. Bagels are identified by a simple identity number (e.g. 14).

Bagels can be eaten.

When they are eaten, they need to be removed from then inventory, via a HTTP DELETE request, which makes sense because it is a delete.

So suppose bagel #14 is eaten, and you send HTTP DELETE /bagels/14. If the bagel exists, it gets removed from the database. If the bagel doesn’t exist, nothing happens. Hence, if you send the HTTP DELETE request once, it will succeed and remove bagel #14. If you send the HTTP DELETE request 10 times, the first will succeed, the last 9 will fail, and bagel #14 will still be removed from the database.

The state between the first DELETE and the 10th DELETE, as far as the database is concerned, is identical.

So, this DELETE operation is idempotent.

So what does HTTP say?

The HTTP 1.1 specification states that the operations GET, HEAD, PUT and DELETE are idempotent, while POST is not. And that is the key difference behind a well-designed PUT vs. POST API endpoint.

So, you should be designing your REST API with idempotency in mind.

Let’s use another example, this time with creation as the focus.

Say you can also add bagels as they come out of the oven. When a bagel is made (created), you need to issue a HTTP request to your inventory server to add the bagel. Else who knows where that bagel went? So, do you use a PUT, or a POST?

The answer is that it depends on how you design your server.

Design 1

Suppose I decide that when bagels come out of the oven, they are immediately tagged with a number, courtesy of a friendly monkey. Then, taking the number of that bagel assigned by the monkey, I issue:

PUT /bagels/<id>

So if we just made bagel #201 (the monkey says it’s #201), then the system adds bagel #201 to the inventory. If we issue a hundred PUT /bagels/201 to the server, exactly one bagel #201 gets added to the inventory. This way of designing how we add a bagel to our inventory suggests that the action of adding a bagel is idempotent. Hence, we use a PUT method.

Design 2

Or suppose I really dislike monkeys labelling my precious bagels. I decide that my trusty server should be in charge of giving an identification number to my bagels, and all I care is that I have a fresh new (unlabelled) bagel to add to the inventory. So I might do something like:

POST /bagels

Why no ID? Because I don’t have one. The server is going to give it one. Why POST? Let’s see. What happens if I make one bagel? I issue:

POST /bagels

What happens if I make 5 bagels? I issue:

POST /bagels
POST /bagels
POST /bagels
POST /bagels
POST /bagels

So, clearly, the effect if sending 1 and 5 “add” requests is different. The first adds exactly one bagel to the inventory. The second adds five. This way of adding bagels is not idempotent. So it should be a POST method.

What about updating?

The same idea applies whether or not were talking about creation (as in the example above), or updates. I bring these two up because these are the two most often confused methods when trying to decide between PUT and POST.

When you’re updating something, say, taking an existing bagel and adding sugar to it. You’ll be deciding between:

PUT /bagels/<id>/addsugar

and

POST /bagels/<id>/addsugar

Again, it’s how you design against what “adding sugar” means.

If you only add sugar once, with exactly 5mg of sugar heated and poured onto the bagel, then you should be using PUT, and rejecting all further requests to add sugar to the same bagel that already has sugar on it.

If you want to allow sugar to be heaped and heaped on top of the same bagel, then you should be using POST.

Same idea – idempotency.

How about relating to CRUD?

Which brings us back to the beginning. REST and CRUD design principles are not identical. You should not forcibly equate them, unless what you’re designing just lends itself to them being equal, which most often is not.

So, design your REST APIs so that they respect the idea of idempotency. The HTTP specification dictates which operations are idempotent (practically all that you’re probably concerned with), and which are not (POST).

It’s my API, why should I care what the standard says?

The great thing about standards is that everybody who chooses to follow them has the advantage of predictable behavior, and predictability cannot be over-emphasized when designing good software.

If your REST API breaks these principles, your users, and people who are writing code to work with your REST API will have a hard time at best, and totally break things at worst.

That’s not good.