Learn/Advanced Topics

JSON Patch & JSON Pointer — RFC 6902 & 6901

JSON Pointer lets you address any value in a document by path. JSON Patch uses those paths to describe precise changes — add, remove, replace, move, copy, and test. Together, they power partial API updates, real-time collaboration, and audit logs.

JSON Pointer (RFC 6901)

A JSON Pointer is a string that identifies a specific value within a JSON document. It always starts with / and uses / to separate path segments:

Documentjson
1{
2 "users": [
3 { "name": "Alice", "age": 30 },
4 { "name": "Bob", "age": 25 }
5 ],
6 "meta": {
7 "version": "2.1"
8 }
9}
PointerResolved Value
"" (empty string)The entire document (root)
"/users"The users array
"/users/0"{ "name": "Alice", "age": 30 }
"/users/0/name""Alice"
"/users/1/age"25
"/meta/version""2.1"

Escaping Special Characters

Two characters need escaping in JSON Pointer:

CharacterEscape SequenceExample Key → Pointer
~ (tilde)~0"a~b" → "/a~0b"
/ (slash)~1"a/b" → "/a~1b"

Tip

Always unescape in reverse order: first replace ~1 with /, then ~0 with ~.

JSON Patch (RFC 6902)

A JSON Patch document is an array of operations. Each operation has an op and a path (JSON Pointer):

JSON Patch Operations

All Six Operations — With Examples

1. add

Inserts a value at the target path. If the path points to an array index, the value is inserted before that index.

Add a new fieldjson
1{ "op": "add", "path": "/users/0/email", "value": "[email protected]" }
Add to array at index 1json
1{ "op": "add", "path": "/users/1", "value": { "name": "Charlie", "age": 28 } }

Note

Use /array/- to append to the end of an array (the - character means “after the last element”).

2. remove

Deletes the value at the target path. The path must exist or the operation fails.

1{ "op": "remove", "path": "/users/1" }

3. replace

Replaces the value at the target path. Equivalent to a remove followed by an add at the same path.

1{ "op": "replace", "path": "/users/0/age", "value": 31 }

4. move

Removes the value at from and adds it at path.

1{ "op": "move", "from": "/users/0/name", "path": "/users/0/displayName" }

5. copy

Copies the value at from to path (without removing the original).

1{ "op": "copy", "from": "/users/0/name", "path": "/meta/lastUser" }

6. test

Asserts that the value at path equals value. If the assertion fails, the entire patch is rejected. Use this for optimistic concurrency control:

Conditional updatejson
1[
2 { "op": "test", "path": "/meta/version", "value": "2.1" },
3 { "op": "replace", "path": "/meta/version", "value": "2.2" }
4]

If /meta/version is not "2.1", neither operation executes.

Complete Patch Example

Original documentjson
1{
2 "title": "My Post",
3 "tags": ["json", "api"],
4 "published": false,
5 "views": 0
6}
Patch operationsjson
1[
2 { "op": "replace", "path": "/title", "value": "My Updated Post" },
3 { "op": "add", "path": "/tags/-", "value": "tutorial" },
4 { "op": "remove", "path": "/views" },
5 { "op": "replace", "path": "/published", "value": true },
6 { "op": "add", "path": "/updatedAt", "value": "2026-04-02T10:00:00Z" }
7]
Result after applying patchjson
1{
2 "title": "My Updated Post",
3 "tags": ["json", "api", "tutorial"],
4 "published": true,
5 "updatedAt": "2026-04-02T10:00:00Z"
6}

JSON Merge Patch (RFC 7396)

JSON Merge Patch is a simpler alternative. Instead of an array of operations, you send a partial document. Present keys are updated, keys set to null are deleted, missing keys are unchanged.

Merge Patch documentjson
1{
2 "title": "My Updated Post",
3 "views": null,
4 "published": true
5}
FeatureJSON Patch (RFC 6902)JSON Merge Patch (RFC 7396)
FormatArray of operationsPartial document
Set a value to nullYes — use replaceNo — null means "delete"
Array manipulationPrecise index controlReplace entire array only
AtomicityBuilt-in with "test"No built-in assertion
Content-Typeapplication/json-patch+jsonapplication/merge-patch+json
ComplexityHigherMuch simpler

Tip

Use JSON Merge Patch for simple field updates. Use JSON Patch when you need precise array edits, conditional updates (test), or audit-friendly operation logs.

Using JSON Patch in APIs

Express.js PATCH endpointjavascript
1import express from 'express';
2import { applyPatch, validate } from 'fast-json-patch';
3
4const app = express();
5app.use(express.json());
6
7app.patch('/api/posts/:id', async (req, res) => {
8 const post = await db.posts.findById(req.params.id);
9 if (!post) return res.status(404).json({ error: 'Not found' });
10
11 const errors = validate(req.body, post);
12 if (errors) return res.status(400).json({ error: errors.message });
13
14 const { newDocument } = applyPatch(post, req.body, true, false);
15 await db.posts.update(req.params.id, newDocument);
16
17 res.json(newDocument);
18});

Try It — Validate a Patch Document

Try It Yourself

A valid JSON Patch document — each operation needs op and path

Frequently Asked Questions

What is the difference between JSON Patch and JSON Merge Patch?
JSON Patch (RFC 6902) is an array of explicit operations (add, remove, replace, etc.) with precise paths. JSON Merge Patch (RFC 7396) is a simpler format where you send a partial document — keys present are set, keys set to null are deleted. Merge Patch cannot set a value to null, distinguish between missing and null, or manipulate arrays precisely.
What is a JSON Pointer?
JSON Pointer (RFC 6901) is a string syntax for identifying a specific value within a JSON document, like "/users/0/name". It uses "/" as a separator and "~0" for literal tildes, "~1" for literal slashes. It is used by JSON Patch to target where operations apply.
Can JSON Patch reorder array elements?
Not directly. There is no "reorder" operation. You can achieve reordering by combining "remove" and "add" operations, or by using "move" to relocate an element from one index to another.
Is JSON Patch atomic?
Yes, by specification. If any operation in the patch array fails, the entire patch must be rolled back and the document should remain unchanged. This makes it safe for concurrent editing scenarios.
How do I escape special characters in JSON Pointer?
Tilde (~) is escaped as ~0, forward slash (/) is escaped as ~1. For example, the key "a/b~c" would be addressed as "/a~1b~0c". These are the only two escape sequences in JSON Pointer.