JSON Patch and Merge Patch
When you want to update part of a JSON document, you have three options: replace the whole document, use JSON Patch, or use JSON Merge Patch. The last two are standard formats that describe only what changed — like a list of instructions, not a full replacement.
The Problem: Partial Updates in APIs
Imagine a user profile with 20 fields. The user changes only their phone number. Without a patch format, you send all 20 fields back to the server just to update one. This wastes bandwidth and risks overwriting fields that changed on the server between your read and your write.
Think of it like editing a book. Instead of reprinting the entire book when you fix a typo on page 47, you send a correction slip: "Page 47, Line 3, replace 'teh' with 'the'." That correction slip is the patch.
HTTP Methods Used with Patches
PUT -- Replace the entire resource with the new JSON
PATCH -- Update part of the resource using a patch document
PATCH with JSON Patch or Merge Patch is the standard way to do partial updates in REST APIs.
JSON Merge Patch (RFC 7396)
JSON Merge Patch is simple. You send a JSON object that looks like the original but contains only the fields you want to change. The server merges your patch into the existing document.
Rules for JSON Merge Patch
- Fields in the patch with values replace the original values.
- Fields in the patch with
nullvalues are deleted from the original. - Fields not mentioned in the patch are left unchanged.
Original Document
{
"name": "Nisha Patel",
"email": "nisha@example.com",
"phone": "9876543210",
"city": "Ahmedabad",
"newsletter": true
}
Merge Patch: Change Phone, Delete Newsletter
{
"phone": "9123456789",
"newsletter": null
}
Result After Applying Merge Patch
{
"name": "Nisha Patel",
"email": "nisha@example.com",
"phone": "9123456789",
"city": "Ahmedabad"
}
The phone number changed. The newsletter field was deleted (because its patch value was null). Everything else stayed the same.
Sending a Merge Patch with Fetch
const patch = {
phone: "9123456789",
newsletter: null
};
fetch('/api/users/42', {
method: 'PATCH',
headers: {
'Content-Type': 'application/merge-patch+json' // Important header
},
body: JSON.stringify(patch)
});
Diagram: Merge Patch Operation
Original Document Merge Patch Result
------------------ ----------- ------
name: "Nisha Patel" (not mentioned) name: "Nisha Patel" ← unchanged
email: "nisha@.." (not mentioned) email: "nisha@.." ← unchanged
phone: "9876543210" + phone: "9123456789" = phone: "9123456789" ← updated
city: "Ahmedabad" (not mentioned) city: "Ahmedabad" ← unchanged
newsletter: true newsletter: null (deleted) ← removed
Limitation of Merge Patch
JSON Merge Patch cannot add an item to an array or remove a specific item from an array. If you send an array in the patch, it replaces the entire array. For precise array operations, use JSON Patch instead.
JSON Patch (RFC 6902)
JSON Patch is more powerful but more verbose. A patch is an array of operation objects. Each operation specifies exactly what to do: add, remove, replace, move, copy, or test.
JSON Patch Operations
Operation Description
--------- -----------
add Add a new field or array item
remove Remove a field or array item
replace Change the value of an existing field
move Move a field to a new location
copy Copy a field to a new location
test Check that a value equals the expected value before applying
Original Document
{
"productId": "P-881",
"name": "Desk Lamp",
"price": 799,
"tags": ["lighting", "desk"],
"manufacturer": {
"company": "BrightCo",
"country": "India"
}
}
JSON Patch Document
[
{ "op": "replace", "path": "/price", "value": 899 },
{ "op": "add", "path": "/tags/-", "value": "energy-saving" },
{ "op": "remove", "path": "/manufacturer/country" },
{ "op": "add", "path": "/discount", "value": 50 }
]
Result After Applying JSON Patch
{
"productId": "P-881",
"name": "Desk Lamp",
"price": 899,
"tags": ["lighting", "desk", "energy-saving"],
"manufacturer": {
"company": "BrightCo"
},
"discount": 50
}
JSON Patch Path Syntax
JSON Patch uses JSON Pointer (RFC 6901) for paths. A path starts with / and uses / to separate levels.
Path Refers To
---- ---------
/price The "price" field at root level
/manufacturer The "manufacturer" object
/manufacturer/company The "company" field inside manufacturer
/tags/0 The first element of the tags array
/tags/- Append to the end of the tags array
Special Characters in Paths
If a key contains / or ~, they are escaped as ~1 and ~0 respectively.
Key: "a/b" → Path: /a~1b
Key: "a~b" → Path: /a~0b
The test Operation
The test operation checks a value before applying the patch. If the test fails, the entire patch is rejected. This prevents accidental updates when data has changed between your read and write.
[
{ "op": "test", "path": "/price", "value": 799 },
{ "op": "replace", "path": "/price", "value": 899 }
]
This patch first checks that the current price is 799. If it is, the price changes to 899. If the price changed to something else (maybe another request updated it), the entire patch is rejected — nothing changes.
Applying JSON Patch in JavaScript
Use the fast-json-patch library:
npm install fast-json-patch
const jsonpatch = require('fast-json-patch');
const document = {
name: "Desk Lamp",
price: 799,
tags: ["lighting", "desk"]
};
const patch = [
{ op: "replace", path: "/price", value: 899 },
{ op: "add", path: "/tags/-", value: "energy-saving" }
];
const result = jsonpatch.applyPatch(document, patch).newDocument;
console.log(result);
Applying JSON Patch in Python
pip install jsonpatch
import json
import jsonpatch
document = {
"name": "Desk Lamp",
"price": 799,
"tags": ["lighting", "desk"]
}
patch = jsonpatch.JsonPatch([
{"op": "replace", "path": "/price", "value": 899},
{"op": "add", "path": "/tags/-", "value": "energy-saving"}
])
result = patch.apply(document)
print(result)
Sending JSON Patch with Fetch
const patch = [
{ op: "replace", path: "/price", value: 899 },
{ op: "add", path: "/tags/-", value: "energy-saving" }
];
fetch('/api/products/P-881', {
method: 'PATCH',
headers: {
'Content-Type': 'application/json-patch+json' // JSON Patch content type
},
body: JSON.stringify(patch)
});
JSON Patch vs JSON Merge Patch
Feature JSON Merge Patch JSON Patch
------- ---------------- ----------
Simplicity Very simple More complex
Array item operations Cannot (replaces array) Full support (add/remove items)
Delete a field Set to null "op": "remove"
Conditional updates Not supported Supported via "test"
Verbosity Low High
Best for Simple field updates Complex structural changes
Content-Type application/merge-patch+json application/json-patch+json
When to Use Each
Use JSON Merge Patch when you update simple flat fields or want to delete keys, and your data does not involve precise array operations. Use JSON Patch when you need to add or remove array items by position, move or copy fields, or apply conditional updates with the test operation.
Summary
JSON Merge Patch sends a partial document — fields present replace originals, fields set to null are deleted, missing fields stay unchanged. JSON Patch sends an array of operations — add, remove, replace, move, copy, and test — each targeting a specific path. Both formats use the HTTP PATCH method with different Content-Type headers. Merge Patch is simpler. JSON Patch is more powerful for complex structural changes and array operations.
