Free online JSON formatter, validator, and developer tools.
A common REST API mistake is requiring clients to send the entire resource just to change one field โ update a user's email and you resend their name, address, and every other field unchanged. Two IETF standards solve this: JSON Patch (RFC 6902), which describes an ordered list of precise operations, and JSON Merge Patch (RFC 7396), which describes updates as a partial object to be merged. They solve overlapping problems differently, and picking the wrong one for your use case leads to real bugs โ especially around deleting fields and updating arrays.
JSON Merge Patch is the simpler of the two. You send a JSON object containing only the fields you want to change, and the server merges it into the existing resource:
/* Original resource */
{
"name": "Alice",
"email": "alice@old.com",
"role": "member",
"address": { "city": "Boston", "zip": "02101" }
}
/* Merge Patch โ Content-Type: application/merge-patch+json */
{
"email": "alice@new.com"
}
/* Result after merge */
{
"name": "Alice",
"email": "alice@new.com",
"role": "member",
"address": { "city": "Boston", "zip": "02101" }
}
The merge algorithm is recursive for nested objects but replaces arrays wholesale โ you cannot patch a single array element with Merge Patch, only replace the entire array. To delete a field, set its value to null in the patch:
/* Merge Patch to remove the "role" field entirely */
{ "role": null }
/* Result */
{
"name": "Alice",
"email": "alice@old.com",
"address": { "city": "Boston", "zip": "02101" }
}
This null-means-delete rule is the single most important thing to remember about Merge Patch โ and it's also its biggest limitation: if a field in your actual data model is legitimately supposed to hold the value null, Merge Patch cannot represent "set this field to null" versus "remove this field" as distinct operations. Both look identical on the wire.
JSON Patch takes a different approach: instead of describing the end state, you describe the exact operations to apply, in order, using JSON Pointer (RFC 6901) paths to address specific locations:
/* JSON Patch โ Content-Type: application/json-patch+json */
[
{ "op": "replace", "path": "/email", "value": "alice@new.com" },
{ "op": "remove", "path": "/role" },
{ "op": "add", "path": "/tags", "value": ["verified"] }
]
JSON Patch defines six operations:
add โ inserts a value at the path; for an array index, inserts and shifts subsequent elements rather than overwritingremove โ deletes the value at the path (explicit, unambiguous โ unlike Merge Patch's overloaded null)replace โ equivalent to a remove followed by an add at the same pathmove โ removes the value at from and adds it at pathcopy โ copies the value at from to path without removing the sourcetest โ asserts the value at path equals the given value; the whole patch fails if the assertion doesn't hold โ useful for optimistic-concurrency checksBecause operations target arrays by index with real insert/remove semantics, JSON Patch can express array edits that Merge Patch simply cannot:
/* Original */
{ "tags": ["draft", "internal"] }
/* JSON Patch โ insert "reviewed" at index 1, without touching the rest */
[ { "op": "add", "path": "/tags/1", "value": "reviewed" } ]
/* Result */
{ "tags": ["draft", "reviewed", "internal"] }
The test operation is worth calling out specifically โ it's how JSON Patch supports safe conditional updates without a separate ETag mechanism:
[
{ "op": "test", "path": "/role", "value": "member" },
{ "op": "replace", "path": "/role", "value": "admin" }
]
/* If /role is not currently "member", the entire patch is rejected โ
protects against a lost-update race where the resource changed
between when the client read it and when it sent the patch. */
| Aspect | JSON Merge Patch | JSON Patch |
|---|---|---|
| Format | Partial object | Array of operations |
| Complexity | Simple โ easy to hand-write | More verbose, needs a library |
| Array edits | Replace whole array only | Insert/remove/move individual elements |
| Deleting a field | Set to null (ambiguous with real nulls) | Explicit remove op |
| Conditional updates | Not supported | test op for optimistic concurrency |
| Content-Type | application/merge-patch+json | application/json-patch+json |
Use Merge Patch for typical "edit a form, submit changed fields" flows where your data model rarely stores meaningful null values and you never need surgical array edits. Use JSON Patch when you need precise array manipulation, explicit field removal that can't be confused with setting a null, or safe concurrent updates via test.
Both formats are delivered via HTTP PATCH (not PUT, which implies full replacement), distinguished by Content-Type. A minimal Node.js/Express handler for Merge Patch looks like:
const mergePatch = require('json-merge-patch');
app.patch('/users/:id', express.json({ type: 'application/merge-patch+json' }), (req, res) => {
const existing = getUser(req.params.id);
const updated = mergePatch.apply(existing, req.body);
saveUser(req.params.id, updated);
res.json(updated);
});
And for JSON Patch, using the fast-json-patch library:
const jsonpatch = require('fast-json-patch');
app.patch('/users/:id', express.json({ type: 'application/json-patch+json' }), (req, res) => {
const existing = getUser(req.params.id);
const result = jsonpatch.applyPatch(existing, req.body);
saveUser(req.params.id, result.newDocument);
res.json(result.newDocument);
});
In both cases, validate the patch against your resource's JSON Schema after applying it โ a syntactically valid patch can still produce an object that violates your business rules (an add that introduces an unexpected field, a replace that sets a string field to an invalid enum value). See our JSON Schema Generator if you need to scaffold that validation schema from a sample resource.
JSON Merge Patch and JSON Patch both let clients send only what changed instead of a full resource, but they're not interchangeable. Merge Patch is simpler and fine for flat, form-like updates; JSON Patch is more expressive, supports precise array operations and explicit field removal, and adds optimistic-concurrency control via test. Pick based on whether your updates ever need surgical array edits or safe conditional application โ if not, Merge Patch's simplicity usually wins.
Comparing a resource before and after a patch is applied? Use our JSON Compare tool to see exactly what changed.