fast-json-patch review
fast-json-patch 3.1.1 applies the RFC 6902 operations add, remove, replace, move, copy, and test to JavaScript data. It can also compare two JSON-compatible values or observe an object and emit operations after mutations. The current release dates from the 2022 prototype-pollution fix. Our Node 22 checks found a dependency-free CommonJS package that loads through both require and ESM import, includes TypeScript declarations, and produces a 3.8 KB gzipped whole-package browser bundle.
fast-json-patch 3.1.1 installed in 0.7 seconds and added 3.8 KB gzipped in our sandbox, with no dependencies or audit findings. Use it for an existing RFC 6902 contract, but put authorization and explicit mutation policy around every untrusted patch.
We installed it
| Install | ✓ · 0.7s | 1 package on disk · 1 MB |
| Import | ✓ | ESM import works · require() works · CommonJS package |
| Browser | 3.8 KB | gzipped (10.8 KB minified), bundled with esbuild |
| Types | ✓ | TypeScript types bundled |
| Known vulns | 0 | 0 critical · 0 high · 0 moderate · 0 low (npm audit) |
Answers from our run
Does fast-json-patch install cleanly?
Yes. In a fresh container with an empty cache, npm install fast-json-patch finished in 0.7s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.
How much does fast-json-patch add to a browser bundle?
3.8 KB gzipped (10.8 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.
Does fast-json-patch work with both ESM and CommonJS?
Yes. Both import 'fast-json-patch' and require('fast-json-patch') worked in Node 22 in our run. The package is published as CommonJS.
Does fast-json-patch include TypeScript types?
Yes, type declarations ship inside the package, so no @types install is needed.
fast-json-patch or rfc6902: which should you use?
rfc6902: Use it when you want another direct RFC 6902 implementation with a newer published line. fast-json-patch 3.1.1 installed in 0.7 seconds and added 3.8 KB gzipped in our sandbox, with no dependencies or audit findings.
When should you not use fast-json-patch?
Only a few business fields may change. An explicit update object is easier to authorize than arbitrary JSON Pointer paths.
Use it if
- Your client and server already exchange RFC 6902 arrays and must agree on pointer and operation semantics.
- A test operation should stop a stale optimistic update before any following replacement is accepted.
- You need structural diffs between JSON values or patches generated from observed object changes.
- Node and browser code need the same small implementation with TypeScript declarations included.
- Only a few business fields may change. An explicit update object is easier to authorize than arbitrary JSON Pointer paths.
- Requests are untrusted and no path allowlist exists. A syntactically valid patch can still replace a field the caller must not control.
- New release activity is mandatory. Version 3.1.1 has remained current since March 2022, despite later repository pushes.
- Arrays represent records with stable IDs. RFC 6902 addresses them by current index, so concurrent insertion can redirect a move or remove.
- Your package policy requires an exports map and an ESM-first contract. This release is CommonJS without an exports map.
Setup reality
We installed fast-json-patch 3.1.1 in a clean Node 22 Bookworm container in 0.7 seconds. npm left one package and 1 MB on disk. The package declares zero direct dependencies and zero peers, occupies 236 KB unpacked, and bundles TypeScript declarations. npm audit found zero vulnerabilities at all four severities. Both require and ESM import succeeded in our sandbox. The published format is CommonJS without an exports map, and the license is MIT.
Our esbuild measurement for a whole-package browser import was 10.8 KB minified and 3.8 KB gzipped. The README still contains a direct index.mjs import and Node 12 era module instructions, so test the exact entry your production bundler selects. No service account, file, or environment variable is needed.
Policy belongs around the library. applyPatch mutates its document by default, while validation must be requested. Patch values can also keep object references. Pass the mutation choice deliberately, clone values when isolation matters, and leave prototype modification blocking enabled. Validation checks RFC shape and pointer behavior; it does not decide whether a caller may write /role or read a secret through from.
Array pointers are evaluated against the object at apply time. An older /items/3 operation can hit another record after concurrent edits. A failed test throws TEST_OPERATION_FAILED, which an API can map to a conflict response. Add tests for failed pointers, mutation, copy and move authorization, and escaped property names.
Patterns
Validate operations while applying them apply-validated-patch
import { applyPatch } from 'fast-json-patch'
const result = applyPatch(document, [
{ op: 'replace', path: '/name', value: 'Ada' },
{ op: 'add', path: '/tags/-', value: 'math' },
], true)
console.log(result.newDocument)The third argument enables validation. The document is still mutated because the fourth argument defaults to true.
Return a patched copy preserve-source
const next = applyPatch(document, patch, true, false).newDocumentThe fourth argument set to false preserves the source object. Clone patch values too if shared references are unacceptable.
Inspect one validation failure validate-sequence
const error = validate(patch, document)
if (error) throw new Error(error.name + ' at operation ' + error.index)The current declaration returns one JsonPatchError or undefined, rather than a list of every error.
Allowlist both write and source pointers authorize-paths
const allowed = new Set(['/profile/name', '/profile/timezone'])
for (const op of patch) {
for (const path of [op.path, op.from].filter(Boolean)) {
if (!allowed.has(path)) throw new Error('forbidden patch path')
}
}Checking only path misses the protected value read by copy or move through from.
Guard an update with test detect-stale-write
const patch = [
{ op: 'test', path: '/version', value: 7 },
{ op: 'replace', path: '/title', value: 'Revised' },
{ op: 'replace', path: '/version', value: 8 },
]A mismatched version throws TEST_OPERATION_FAILED; map that named outcome to a conflict response.
Generate an invertible difference compare-documents
import { compare } from 'fast-json-patch'
const patch = compare(before, after, true)The true flag inserts test operations before destructive changes, so applying against a changed source fails.
Collect operations after object edits observe-mutations
const observer = observe(state)
state.count += 1
const patch = generate(observer, true)
unobserve(state, observer)Generate receives the observer, and unobserve releases tracking when the object is no longer watched.
Encode a property as one pointer segment escape-pointer
const key = 'theme/dark~beta'
const path = '/settings/' + escapePathComponent(key)JSON Pointer encodes tilde as tilde-zero and slash as tilde-one; raw string interpolation can change the target path.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| rfc6902 | npm | Use it when you want another direct RFC 6902 implementation with a newer published line. |
| json8-patch | npm | Use it when immutable application and companion JSON Pointer helpers suit the surrounding code. |
| immutable-json-patch | npm | Use it when immutable document updates are a firm requirement rather than a positional option. |
More utils guides
lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

