mrkeyoor.com_
Sat 08 Aug 17:39 UTC
npmUtilsupdated 08 Aug 2026

fast-json-patch

fast-json-patch is a dependency-free JavaScript implementation of JSON Patch, the RFC 6902 format for describing changes to JSON documents. It applies add, remove, replace, move, copy, and test operations, validates patches, compares two documents to create a patch, and can observe object mutations to generate operations. It supports CommonJS and ES modules and includes TypeScript declarations. Its defaults mutate the input document, validation is opt-in, and prototype modifications are blocked unless a caller deliberately disables that protection.

Verdict

Use fast-json-patch when RFC 6902 is a real interoperability requirement and wrap it with authorization plus validation. Do not introduce JSON Patch just to avoid writing a small update object, and weigh the stale release before adopting it in new code.

API stability5/5RFC 6902 is stable and the library's apply, compare, observe, and validate APIs have not churned, though some defaults are easy to misuse.
Docs3/5The README documents every operation, pointer behavior, validation errors, mutation, security defaults, and examples, but parts of the import guidance and API signatures are stale or inconsistent.
Maintenance2/5The repository was pushed in October 2025 and is not archived, but the published npm version is from March 2022 and 82 issues and PRs remain open.
Ecosystem3/5JSON Patch is an open HTTP standard and the library works across Node, browsers, CommonJS, ESM, and TypeScript, but many APIs prefer simpler merge-style updates.

Use it if

  • Your API already speaks RFC 6902 and you need to apply or produce interoperable JSON Patch arrays
  • You need compact document updates with test operations for optimistic concurrency
  • You want to compare two JSON-compatible values or observe a local object and generate patches
  • You need a zero-dependency implementation that works in Node and browsers
Skip it if

Setup reality

npm install fast-json-patch adds no runtime dependencies and the package contains CommonJS, ESM, browser builds, and declarations. The annoying work is not installation but policy. Validate every untrusted patch, keep prototype protection enabled, authorize allowed pointer paths, decide whether mutation is acceptable, clone patch values when references must not be shared, and handle failed test operations as conflicts.

Patterns

Apply several JSON Patch operationsapply-patch

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

const document = { name: 'Ada', tags: ['math'] }
const patch = [
  { op: 'replace', path: '/name', value: 'Ada Lovelace' },
  { op: 'add', path: '/tags/-', value: 'programming' },
]

const result = applyPatch(document, patch, true)
console.log(result.newDocument)

The third argument enables validation. By default the original document is mutated and - appends to an array.

Return a patched cloneapply-immutably

import { applyPatch, deepClone } from 'fast-json-patch'

const result = applyPatch(
  document,
  deepClone(patch),
  true,
  false,
)
const updated = result.newDocument

The fourth argument disables document mutation. Clone the patch too when its value objects must not become shared references.

Validate before applying untrusted inputvalidate-patch

import { validate } from 'fast-json-patch'

const error = validate(patch, currentDocument)
if (error) {
  throw new Error(`invalid patch at operation ${error.index}: ${error.name}`)
}

validate returns one JsonPatchError or undefined, despite an older README example that treats the result like an array.

Use test for optimistic concurrencyguard-with-test

const patch = [
  { op: 'test', path: '/version', value: 7 },
  { op: 'replace', path: '/title', value: 'Revised title' },
  { op: 'replace', path: '/version', value: 8 },
]

const updated = applyPatch(document, patch, true, false).newDocument

A failed test throws TEST_OPERATION_FAILED. Translate that into a conflict response instead of a generic server error.

Create a patch from two documentscompare-documents

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

const before = { user: { name: 'Ada', active: true } }
const after = { user: { name: 'Ada Lovelace', active: true } }
const patch = compare(before, after, true)

The third argument adds test operations before destructive changes, producing a patch that detects stale source data.

Generate patches from object mutationsobserve-mutations

import { observe, generate, unobserve } from 'fast-json-patch'

const state = { count: 0, labels: [] }
const observer = observe(state)
state.count += 1
state.labels.push('new')
const patch = generate(observer, true)
unobserve(state, observer)

generate takes the observer, not the document. Always unobserve long-lived objects when tracking is finished.

Apply one operationapply-single-operation

import { applyOperation } from 'fast-json-patch'

const result = applyOperation(
  document,
  { op: 'remove', path: '/temporary' },
  true,
  false,
)
console.log(result.removed, result.newDocument)

remove fails validation when the path does not exist; it is not an idempotent delete.

Escape a dynamic JSON Pointer segmentescape-pointer-path

import { escapePathComponent } from 'fast-json-patch'

const fieldName = 'settings/theme~dark'
const path = `/profile/${escapePathComponent(fieldName)}`
const operation = { op: 'replace', path, value: true }

JSON Pointer encodes ~ as ~0 and / as ~1. Never concatenate an unescaped user-supplied property name.

Move an array elementmove-array-item

const patch = [
  { op: 'move', from: '/items/3', path: '/items/1' },
]
const reordered = applyPatch(document, patch, true, false).newDocument

Array pointers are indexes at apply time, so concurrent insertions can make a previously created reorder patch target the wrong item.

Restrict writable paths before applicationauthorize-paths

const allowedRoots = ['/profile/name', '/profile/timezone']
for (const operation of patch) {
  const paths = [operation.path, operation.from].filter(Boolean)
  if (paths.some(path => !allowedRoots.includes(path))) {
    throw new Error('patch path is not allowed')
  }
}
const updated = applyPatch(document, patch, true, false).newDocument

RFC validation checks structure, not authorization. Check both path and from so move or copy cannot read a protected field.

Alternatives

PackageRegistryPick it when
rfc6902npmYou want another standards-focused implementation with a more recently published major release
json8-patchnpmYou want an immutable-oriented JSON Patch implementation and JSON Pointer helpers
jsondiffpatchnpmYou need human-readable object and text diffs with patch and unpatch, not strict RFC 6902 interchange