Guide

JSON Patch in JavaScript

How to apply RFC 6902 JSON Patches in Node.js and the browser - with fast-json-patch, immutable alternatives, and diff generation between two documents.

Choosing a library

The most widely used implementation is fast-json-patch. It covers every RFC 6902 operation (add, remove, replace, move, copy, test) and works in Node and the browser:

npm install fast-json-patch

Applying a patch

applyPatch returns a result object whose newDocument holds the patched document. By default it mutates in place - pass false as the third argument and a cloned document is returned instead:

import { applyPatch } from 'fast-json-patch';

const doc = {
  name: 'demo',
  version: 1,
  tags: ['a', 'b'],
};

const patch = [
  { op: 'replace', path: '/version', value: 2 },
  { op: 'add', path: '/tags/-', value: 'c' },
  { op: 'remove', path: '/name' },
];

const result = applyPatch(doc, patch, true, false);
console.log(result.newDocument);
// { version: 2, tags: ['a', 'b', 'c'] }

The path /tags/- appends to the array. Paths are JSON Pointers (RFC 6901): escape ~ as ~0 and / as ~1 inside keys.

Generating a patch from two documents

compare produces the operations that turn one document into the other - the same output as the JSON Patch Generator:

import { compare } from 'fast-json-patch';

const before = { a: 1, b: { c: 2 } };
const after = { a: 1, b: { c: 3, d: 4 } };

console.log(compare(before, after));
// [
//   { op: 'replace', path: '/b/c', value: 3 },
//   { op: 'add', path: '/b/d', value: 4 },
// ]

Guarding with the test operation

A failing test operation stops evaluation at that point. Use it as a precondition so a patch only lands on the state it was built for:

const patch = [
  { op: 'test', path: '/version', value: 1 },
  { op: 'replace', path: '/version', value: 2 },
];

try {
  applyPatch(doc, patch, true);
} catch (err) {
  // JsonPatchError: test failed, path missing, or type mismatch
  console.error('patch rejected:', err.message);
}

Common pitfalls

  • Mutation surprises. applyPatch(doc, patch) mutates doc unless you request a clone. In React state, always use the immutable form or you will miss re-renders.
  • Strict test equality. A test on 1 fails against '1'. JSON types must match exactly.
  • Array indices shift after remove. Removing index 0 renumbers everything after it. Remove from the highest index down, or guard with test operations.
  • Evaluation stops on error. RFC 6902 does not guarantee rollback of earlier operations; check the library's mutation behavior before treating a patch as a transaction.

Prototype patches visually

Paste your two documents into the JSON Patch Generator to get the exact operations, or inspect changes line by line with the JSON Diff tool. Then hand the patch to applyPatch in your app or API client.

Background on the format: JSON Patch documentation and real-world examples.