JSON PatchJSON Merge PatchRFC 6902RFC 7396REST APIPATCH

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

·7 min read·Advanced

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?

CriteriaJSON Merge PatchJSON Patch
SimplicityVery simpleMore complex
Null handlingnull means deletenull is a valid value
Array operationsReplace entire arrayAdd, remove, move items
Optimistic lockingNot supportedtest operation
Content-Typeapplication/merge-patch+jsonapplication/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. If you need to generate a JSON Patch document without writing the diff logic yourself — or apply one and see the result — JSONKit's JSON Patch Generator does both directly in your browser: paste an original and updated document to get an RFC 6902 patch back, or paste a target document and a patch to see it applied.

Try JSON Diff

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