JSON PatchJSON Merge PatchRFC 6902RFC 7396REST APIPATCH

JSON Patch vs JSON Merge Patch: Updating JSON the Right Way

·7 min read

The Problem: Updating JSON Without Replacing It All

REST APIs conventionally use PUT to replace an entire resource. If you want to change one field, you must send the full document. This is wasteful and causes race conditions when two clients update different fields simultaneously.

PATCH was added to HTTP to solve this — but HTTP doesn't specify what the PATCH body should look like. Two RFCs provide standardized formats: JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7396).

JSON Merge Patch (RFC 7396) — The Simple One

Merge Patch is the intuitive approach: send an object that looks like a partial update. Fields you include are set. Fields you omit are unchanged. To delete a field, set it to null.

Example: Change a user's city and remove their phone number:

json
PATCH /users/usr_9k2mXpQr4t
Content-Type: application/merge-patch+json

{
  "address": {
    "city": "Mumbai"
  },
  "phone": null
}

Original document:

json
{
  "name": "Ravi Mehta",
  "phone": "+91-98765-43210",
  "address": {
    "city": "Ahmedabad",
    "state": "Gujarat"
  }
}

Result after merge patch:

json
{
  "name": "Ravi Mehta",
  "phone": null,
  "address": {
    "city": "Mumbai",
    "state": "Gujarat"
  }
}

Limitation: You cannot use null as a legitimate value — null always means "delete this field." If your schema uses null intentionally, use JSON Patch instead.

JSON Patch (RFC 6902) — The Precise One

JSON Patch describes changes as a sequence of operations. Each operation has a type (op), a target path (path in JSON Pointer format), and optionally a value.

The six operation types:

json
[
  { "op": "add",     "path": "/tags/0",      "value": "premium" },
  { "op": "remove",  "path": "/phone" },
  { "op": "replace", "path": "/address/city","value": "Mumbai" },
  { "op": "move",    "path": "/lastName",    "from": "/surname" },
  { "op": "copy",    "path": "/billingCity", "from": "/address/city" },
  { "op": "test",    "path": "/name",        "value": "Ravi Mehta" }
]

The test operation is the key advantage over Merge Patch. It asserts a value before applying changes — if the test fails, the entire patch is rejected. This enables optimistic concurrency:

json
[
  { "op": "test",    "path": "/version", "value": 7 },
  { "op": "replace", "path": "/status",  "value": "published" },
  { "op": "replace", "path": "/version", "value": 8 }
]

If another client updated the document and incremented version to 8 before this patch arrives, the test fails and the patch is rejected — no lost update.

Applying JSON Patch in JavaScript

javascript
import { applyPatch } from "fast-json-patch";

const doc = { name: "Ravi", role: "member", score: 10 };
const patch = [
  { op: "replace", path: "/role", value: "admin" },
  { op: "add",     path: "/lastLogin", value: "2025-01-15T14:00:00Z" }
];

const result = applyPatch(doc, patch).newDocument;
// { name: "Ravi", role: "admin", score: 10, lastLogin: "2025-01-15T14:00:00Z" }

Applying JSON Merge Patch in JavaScript

javascript
function mergePatch(target, patch) {
  const result = { ...target };
  for (const [key, value] of Object.entries(patch)) {
    if (value === null) {
      delete result[key];
    } else if (typeof value === "object" && !Array.isArray(value)) {
      result[key] = mergePatch(result[key] ?? {}, value);
    } else {
      result[key] = value;
    }
  }
  return result;
}

Which Should You Use?

| Criteria | JSON Merge Patch | JSON Patch | |---|---|---| | Simplicity | Very simple | More complex | | Null handling | null means delete | null is a valid value | | Array operations | Replace entire array | Add, remove, move items | | Optimistic locking | Not supported | test operation | | Content-Type | application/merge-patch+json | application/json-patch+json |

Use Merge Patch when your updates are simple, arrays are always replaced wholesale, and null is not a meaningful value in your schema.

Use JSON Patch when you need precise array surgery, optimistic concurrency control, or when null is a valid field value your clients need to set.

Both are well-supported — Express, FastAPI, Rails, and Spring all have libraries for applying both formats server-side.

Try JSON Diff

Visualise the before and after of a patch operation side by side.