Aug 14th, 2024

PUT vs. PATCH: It Is About Time To Learn the Difference

Understanding the difference between PUT and PATCH can help you design better applications.

Full Resource Replacement With PUT

PUT requests replace the entire resource with the provided data. PUT requests require sending the complete representation of the resource. PUT is idempotent, meaning multiple identical requests result in the same resource state.

bash
PUT /users/123
Content-Type: application/json
{
"id": 123,
"firstName": "Pablo",
"lastName": "Picasso"
}
bash
PUT /users/123
Content-Type: application/json
{
"id": 123,
"firstName": "Pablo",
"lastName": "Picasso"
}

Example response:

bash
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"firstName": "Pablo",
"lastName": "Picasso"
}
bash
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"firstName": "Pablo",
"lastName": "Picasso"
}

PUT request may fail for a number of reasons. These include missing or invalid data (HTTP 400), server errors (HTTP 500), missing resources (HTTP 404) and more.

No matter if you send one PUT request or many. It should always yield the same result (unless you get rate limited) and resource state. This concept is called idempotency.

PUT Requests And The Real World

While this definition sounds easy enough, the real world is more complicated. Even with our simple example we run into three oddities:

  • The id of the user is specified in both the URL and the id property of the request body.
  • User records may contain any number of fields that are not meant to be updated like: id, createdAt, updatedAt. Yet we're required to add these fields to our request anyway?
  • User records may contain fields for internal use that are not exposed at all like hashedPassword or hasReceivedXMarketingEmails. Do they need to be present in a PUT request?

In a world where we want to live and die by the spec the answer to these question is probably yes.

Additionally, full resource replacement requires us to do extra work when implementing APIs. For example, we now need to check if the user tried to modify any number of immutable fields.

We're also one field propagation away from accidentally updating an immutable field like our id or hashedPassword in our database.

This frustration sparked the creation of the PATCH method.

As of now, we live in a twilight world. Some production APIs adhere to RFC 2616 (full resource replacement) but most break with it to be more practical. Here are two examples: Paypal, Twillio.

Today, many consumers will consider it idiomatic if PUT requests contain a full list of all mutable fields for a given resource.

bash
PUT /users/123
Content-Type: application/json
{
# This id is gone since it is immutable
"firstName": "Pablo",
"lastName": "Picasso"
}
bash
PUT /users/123
Content-Type: application/json
{
# This id is gone since it is immutable
"firstName": "Pablo",
"lastName": "Picasso"
}

PATCH to the rescue: Some popular production APIs converted all their mutations from PUT to PATCH to allow for more flexibility. Here are two examples: Pinterest, Notion. Others support PATCH and PUT at the same time.

Practical Applications of PATCH

PATCH request allow partial updates of a resource.

In the wild, we often see PATCH requests to update a subset of resource fields. Let's take our example from above. To update the firstName of our user we send the following request:

bash
PATCH /users/123
{
"firstName": "Pablo",
}
bash
PATCH /users/123
{
"firstName": "Pablo",
}

Note that we are including the fields that we want to change and not the full user record.

This is a correct usage of PATCH but does not paint the full picture.

Diving Deeper Into PATCH

Although it is common to see PATCH requests in web applications it is important to note that the PATCH HTTP method is not part of the HTTP RFC 2616. Instead, it is a proposed standard tracked in RFC 5789.

The PATCH method requests that a set of changes described in the request entity be applied to the resource identified by the Request-URI. The set of changes is represented in a format called a "patch document" identified by a media type.

This definition gives us flexibility in describing the changes we want to apply to a record. We are not restricted to the fields of our record (like firstName or lastName). Instead, we can author any set of instructions meant to change the state of our record.

A simple example: Let's imagine our API manages a set of counters. We send requests to increment a counter.

Let's send a couple requests.

bash
PATCH /counters/123
Content-Type: application/json
{
"increment": 1
}
bash
PATCH /counters/123
Content-Type: application/json
{
"increment": 1
}

Response

bash
HTTP/1.1 200 OK
{
"count": 1,
}
bash
HTTP/1.1 200 OK
{
"count": 1,
}

Another request

bash
PATCH /counters/123
Content-Type: application/json
{
"increment": 1
}
bash
PATCH /counters/123
Content-Type: application/json
{
"increment": 1
}

Response

bash
HTTP/1.1 200 OK
{
"count": 2,
}
bash
HTTP/1.1 200 OK
{
"count": 2,
}

There are two things to note about this example:

  • PATCH requests CAN be idempotent but do not have to be. Here, sending the same request many times will generate a different response on every request. In contrast, partially updating our firstName from our user record (s. above) was performed idempotently.
  • PATCH requests CAN describe the properties of the record they are changing (e.g. firstName). They can also be comprised of instructions on how to change a record (e.g. increment: 1).

Conclusion

PUT is used for complete replacements and ensures idempotency. PATCH requests allow partial updates and can be non-idempotent depending on the operations applied. Furthermore, PATCH requests can contain any set of instructions to modify a resource. This set of instructions can be defined by you. This can be as simple as a single field of a resource or as complicated as a file diff.

Understanding these methods' behaviors and appropriate use cases will lead to more effective and efficient API design.

We were fed up with unclear API definitions and bad APIs

So we created a better way. API-Fiddle is an API design tool with first-class support for DTOs, versioning, serialization, suggested response codes, and much more.