Here’s a quick note from building a RESTful API for updating objects in my personal project.

Let’s say we have a transaction object with the following schema

{
	"id": 911,
	"amount": 100,
	"currency": "INR",
	"party": "UBER",
	"note": "Cab from home to Indiranagar",
	"tags": ["cab", "transport", "transit"]
	"timestamp": "2024-01-12 20:08",
}

I want to create an API which allows modifying an existing transaction object. Modifications can be of 2 types.

  1. Completely replace a transaction
  2. Partially update a transaction, for eg; editing just the note field

I usually go with approach 1: creating a PUT /transactions/<id> API that accepts a complete transaction object and replaces the existing one. This offloads most of the work to the client or caller, as they have to send the full transaction object even for minor changes. Most of the time that’s fine. But this time I wanted to explore other options.

Sending partial data in a PATCH API


A PATCH /transaction/<id> API that accepts a transaction object with only those fields that needs to be modified. Only the provided fields are updated in the database, while the rest remain unchanged. This is better in a way since the client doesn’t really need to know about the fields which are not being modified.

In this approach, deletion of fields should be handled carefully. Request body should be parsed correctly to distinguish between “not wanting to update a field” vs “deleting the existing value of a field”. Depending on the framework/language used for the API implementation, making this distinction might not be trivial.

To make this distinction clear, we can follow RFC 7386 - Here we explicitly set NULL for those fields which needs to be deleted.

Callout

Arrays are treated as an atomic object when patching. This means they can only be replaced, not merged

If the patch is anything other than an object, the result will always be to replace the entire target with the entire patch. Also, it is not possible to patch part of a target that is not an object, such as to replace just some of the values in an array.

JSON patch document


A PATCH /transaction/<id> API that will accept a PATCH document as specified in RFC 6902

According to the RFC, the document will contain 3 fields

  1. op - specifies the operation to be done (replace, remove, add etc),
  2. path - json path to the field to which the operation is to be performed,
  3. value - actual content that needs to be used for the specified op

Example

[
    {
        "op": "remove",
        "path": "/note"
    },
    {
        "op": "replace",
        "path": "/party",
        "value": "Namma Yatri"
    }
]

In this example, we do the following

  1. Replace the value at /party with Namma Yatri
  2. Remove the /note field

There are different libraries available in various languages to apply a PATCH document to an existing object. An example in Go is evanphx/json-patch

Callout

RFC 6902 allows granular array operations using array indices in the path (e.g., /tags/0 for first element, /tags/- to append etc). This enables precise array manipulation without replacing the entire array.

Summary


  • PUT – when the client has full object knowledge
  • PATCH (RFC 7386) – when client wants to update a subset of fields simply
  • PATCH (RFC 6902) – when fine-grained control is needed (e.g., arrays)

References


  1. Partial document update - Azure Cosmos DB for NoSQL | Microsoft Learn
  2. Creating a JSON Patch endpoint in Go · Jamie Tanna | Software Engineer (jvt.me)
  3. GitHub - evanphx/json-patch: A Go library to apply RFC6902 patches and create and apply RFC7386 patches