Difference between POST, PUT, and PATCH
Practical guidelines for meaningful use of the modifying HTTP methods with examples.
Technically, we can use POST for everything in the same sense as we can create the whole web with just a DIV element. However, just because we can doesn’t mean we should.
Practitioners of RESTful API design teach us to use the HTTP verbs in a sensible way to achieve a level of semantics for our HTTP endpoints. This semantics helps implementers meet user expectations and gives users insight into the intended use.
While the methods GET, DELETE, and HEAD are easy to understand and usually used correctly, methods POST, PUT, and PATCH are often used interchangeably and insensibly.
Let us define simple guidelines for distinguishing where to use what method when designing an HTTP API.
POST vs PUT and PATCH
While PUT is idempotent, POST is not. By its specification, PATCH is not necessarily idempotent, but it is strongly recommended to make it so wherever it is possible.
Idempotence means that calling the endpoint repetitively will leave the system in the same state. Let us demonstrate this on an example:
Calling POST /product
two times will create two resources:
> POST /product { "name": "Tux mug" } < 201 > POST /product { "name": "Tux mug" } < 201 > GET /product < 200 { "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug" }, { "id": 101, "createdAt": "2021-06-12T14:22:51", "name": "Tux mug" }
While calling PUT /product
two times should only create a single one:
> PUT /product { "name": "Tux mug" } < 201 > PUT /product { "name": "Tux mug" } < 204 > GET /product < 200 { "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug" }
There could also be valid use-cases when we need to modify the resource in a non-idempotent manner. An example would be incrementing a resource’s attribute. Such use-cases are, however, quite rare. Here we would use PATCH:
> PATCH /product/100 { "views": "$increment" } < 204 > GET /product/100 < 200 { "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug", "views": 1 } > PATCH /product/100 { "views": "$increment" } < 204 > GET /product/100 < 200 { "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug", "views": 2 }
PUT vs PATCH
Both PUT and PATCH are meant to modify existing resources. The difference is that PUT modifies the entire resource while PATCH mutates the resource only partially.
To demonstrate this on an example, consider the following Product resource:
{ "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug", "price": 3.5 }
What if a product administrator wants to change the Price value of the Product?
When using PATCH, only the particular attribute is modified:
> PATCH /product/100 { "price": 5.0 } < 204 > GET /product/100 < 200 { "id": 100, "createdAt": "2021-06-12T14:22:49", "name": "Tux mug", "price": 5.0 }
On the contrary, PUT will update the entire resource:
> PUT /product/100 { "price": 5.0 } < 204 > GET /product/100 < 200 { "id": 100, "createdAt": null, "name": null, "price": 5.0 }
A real-world implementation would probably reject such a request as invalid; we have just demonstrated the concept.
Conclusion
Using HTTP methods consistently will establish a well-defined contract between its users and implementers. APIs with clear semantics are easier to follow and evolve.
Idempotent endpoints ensure better robustness of the system and it is desired to make as many resources as possible be idempotent.
Exceptions and edge cases should be well documented and communicated.
Happy end-pointing!