One of the challenges when designing a REST API is choosing the right HTTP method (GET, PUT, POST etc.) that corresponds with the operation being performed. Some people incorrectly assume that they can freely choose any method as long as the client and the server agree on it. This is wrong because a request passes through many intermediaries and middleware applications which perform optimizations based on the HTTP method type. These optimizations depend on two key characteristics of HTTP methods: idempotency and safety, which are defined in the HTTP specification.
Safe HTTP Methods: Safe methods aren’t expected to cause any side effects. These operations are read-only. E.g. querying a database.
Idempotent HTTP Methods: Idempotent methods guarantee that repeating a request has the same effect as making the request once.
Idempotency and safety are properties of HTTP methods that server applications must correctly implement. This means if you are implementing an operation and choose an idempotent HTTP method to invoke the operation, you must ensure that the implementation returns the same result if invoked once or multiple times for the same input.
GET: Idempotent & Safe
GET requests are used for retrieving information. These requests must be idempotent and safe: any operation invoked using GET must not alter the state of any resource.
GET /books
To get a specific book,
GET /books/<title>
POST: Non-Idempotent
POST requests are not idempotent. They are used for creating new resources or updating existing ones. For example, suppose we have a resource called Student
with the following attributes: name
, college
, major
and gpa
. To enroll a new student, we can use POST to create a new resource:
POST /students/ // Create a new student
{
"name": "Michael Scarn",
"college": "Stanford",
"major": "computer science"
}
POST requests are allowed to perform partial updates. For example, to update the GPA of a student, we’ll make the POST request on the specific record (given by the student_id
) and only supply the attribute we want to update:
POST /students/<student_id>
{
"gpa": "3.9"
}
In the above example, if the student_id
doesn’t exist, the application should return HTTP 404: Not Found
error.
PUT: Idempotent
PUT requests are idempotent. This means that identical requests can be repeated multiple times, and the state of the resource on the server doesn’t change any further after the first request. PUT can be used to create a new resource or update an existing one. For updates, PUT requests must contain all the attributes of the resource, unlike POST requests which can have partial attributes. Here’s a PUT request to update a student’s GPA:
PUT /students/<student_id>
{
"name": "Michael Scarn",
"college": "Stanford",
"major": "computer science",
"gpa": "3.9"
}
In the above example, if the student_id
in the PUT request doesn’t exist, the application may create a new record and assign it the student_id
.
If you are wondering why PUT requests must have all the attributes and not just the one we want to update, consider the following hypothetical situation in which we allow partial updates in the PUT request. Suppose that a student is switching majors. The client issues two PUT requests: the first one to update student’s GPA for the existing major and a second request to update the major and reset the GPA. The PUT requests are made in the right order:
PUT /students/<student_id> //Partial update: Violates idempotency contract
{
"gpa": "3.85"
}
PUT /students/<student_id> //Partial update: Violates idempotency contract
{
"major": "engineering",
"gpa": "0.0"
}
Assume that the request for the second requests arrives before the first one which is lost due to a network error. Because the client thinks that PUT requests are idempotent, it may retry the first request a second time, that will incorrectly update the student’s GPA to 3.85 for the new major, leaving the resource in an inconsistent state. It could be very hard to trace and breaks the idempotency contract: multiple invocation result in different states.
PUT /students/<student_id>
{
"name": "Michael Scarn",
"college": "Stanford",
"major": "engineering",
"gpa": "3.85" // GPA updated for the wrong major!
}
Partial updates are important especially if the resource contains a lot of attributes. It is wasteful to first fetch the resource using GET and then send all the attributes when the goal is to update just a few. In reality, a lot of developers allow partial updates with PUT, which is a violation of the idempotency contract. For partial updates, use POST, or as we’ll later see, PATCH.
POST vs PUT - Partial vs Complete Updates to Resource
There is a common misconception that it isn’t RESTful to use the HTTP POST method to update existing resources. People who hold this view suggest that one should always use the PUT method for updates and POST for creating new resources. This isn’t quite right. The REST standard doesn’t stop us from using POST requests for updates. In fact, it doesn’t even talk about it because idempotency and safety guarantees are properties of the HTTP protocol, not of the REST standard. Roy Fielding, writes:
Some people think that REST suggests not to use POST for updates. Search my dissertation and you won’t find any mention of CRUD or POST. The only mention of PUT is in regard to HTTP’s lack of write-back caching. The main reason for my lack of specificity is because the methods defined by HTTP are part of the Web’s architecture definition, not the REST architectural style. […] For example, it isn’t RESTful to use GET to perform unsafe operations because that would violate the definition of the GET method in HTTP, which would in turn mislead intermediaries and spiders.
Therefore, the choice between PUT and POST boils down to one thing: idempotency guarantee of these methods. Because PUT is idempotent, clients or intermediaries can repeat a PUT request if the the response for the first request doesn’t arrive on time, even though the request may have been processed by the server. In order to stay idempotent, PUT requests must replace the entire resource and hence must send all the attributes. For partial updates, POST or PATCH (non-idempotent methods) must be used.
DELETE: Idempotent
DELETE requests are idempotent and are used for deleting a resource. One common confusion people have with DELETE calls is what type of HTTP status code to return on repeat calls. Some people assume that because DELETE is idempotent, it must always return the same HTTP status e.g. HTTP 200
. This assumption is wrong. Although there is no harm in returning the same status code, if your use case requires it, returning a different status code like the HTTP 404: Not Found
doesn’t violate the idempotency contract. Idempotency doesn’t concern itself with what is returned to the client. It refers to the state of some resource on the server. So it is perfectly valid to return HTTP 200: OK
on the first delete call, and HTTP 404: Not Found
on subsequent ones, since in both cases, the resource is deleted and its state isn’t changed on the server side. You might also use HTTP 204: No Content
if the response body is empty.
PATCH: Non-Idempotent
I like to think of PATCH as the non-idempotent cousin of the PUT request. Because it is non-idempotent, it could be used partial updates:
PATCH /students/<student_id> //Partial update: OK
{
"gpa": "3.85"
}
It is up to you whether you want to adopt PATCH for partial updates or use POST.
Here’s a table summarizing the results:
HTTP Method | Idempotent? | Operation? |
---|---|---|
GET | yes | Retrieval or query. |
POST | NO | Create or update resources. Partial updates are allowed. |
PUT | yes | Create or update resources. Partial updates are not allowed. |
DELETE | yes | Delete resources. |
PATCH | NO | For partial updates. |
Comments (2)
will bullow
Umer, I agree with your analysis on DELETE not returning 201 or 204 always. This is absolutely fine because once the document is deleted, the state on the server stays the same which is that the document is deleted. Only the response to users is different.
Roy Fielding (fake)
PUT vs POST is simple and there is a lot of misinformation out there. One that a lot of people believe is that POST should be used for creating resources while PUT is used for updates. The methods should be selected based on the idempotence needs.
Use PUT to completely replace a resource. Do it as many times as you like. Idempotency guarantees that the end result will be the same each time. For example
num = 3
. No matter how many times you run it,num
will always have the value 3. POST on the other hand is likenum++
. The result depends on the value of thenum
at the time increment operator operator was applied.