
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
@api-client/json
Advanced tools
This library provides a robust implementation of JSON Patch (as defined by RFC 6902) for JavaScript applications. It's designed to be used primarily with API clients, enabling efficient and precise data updates. Think of it as a way to send "diffs" or "changes" to a JSON document, rather than sending the entire document every time.
This library is built as an ES module (ESM) and includes advanced features like calculating diffs, reverting changes, and patch rebasing for handling concurrent edits. It builds upon the JSON8 library.
Before diving into the code, let's understand the core ideas:
/foo/bar/0 will point to the first element of bar array, inside foo object).npm install @api-client/json
The most common use case is applying a patch to a document.
import { Patch, Json } from '@api-client/json';
// Our starting document
let myDocument = {
"foo": "bar",
"baz": [1, 2, 3],
};
// A patch that describes how to modify the document
const myPatch = [
{ "op": "replace", "path": "/foo", "value": "new value" },
{ "op": "add", "path": "/baz/-", "value": 4 } // Adds 4 to the end of the array
];
// Apply the patch
const result = Patch.apply(myDocument, myPatch);
// Update the document with new values
myDocument = result.doc;
// myDocument is now: { "foo": "new value", "baz": [1, 2, 3, 4] }
console.log(myDocument);
Important Note on Mutability:
For efficiency, Patch.apply may modify the original document you pass in. If you need to preserve the original, make a copy:
const myOriginalDocument = { foo: 'bar' };
const docCopy = Json.clone(myOriginalDocument); // Create a deep copy
const result = Patch.apply(docCopy, myPatch); // Operate on the copy
// myOriginalDocument is unchanged, docCopy has been updated
Here are the essential functions you'll use most often:
Patch.apply(document, patch, [options])Applies a patch (an array of operations) to a document.
document: The target document (may be mutated).patch: The JSON Patch to apply (an array of operation objects).options: Optional. If { reversible: true } is provided, the return value will include a revert property.Returns an object with a doc property containing the patched document. Due to the JSON Patch specification, this may be a new object, even if the patch didn't modify the document.
The operation is atomic: if any patch operation fails, the document is restored to its original state, and an error is thrown.
import { Patch } from '@api-client/json'
const result = Patch.apply(doc, patch)
doc = result.doc
// With reversible option:
const reversibleResult = Patch.apply(doc, patch, { reversible: true })
doc = reversibleResult.doc
const revertPatch = reversibleResult.revert // Object that can be used to revert the patch
Patch.patch(document, patch, [options])Alias for Patch.apply().
Patch.revert(document, revert)Reverts a patch using a revert object (obtained from Patch.apply with reversible: true).
document: The document to revert.revert: The revert object.Returns an object with a doc property containing the reverted document.
import { Patch } from '@api-client/json';
const myDocument = { a: 1 };
const myPatch = [{ op: 'replace', path: '/a', value: 2 }];
// Apply the patch with revert info
const result = Patch.apply(myDocument, myPatch, { reversible: true });
const patchedDocument = result.doc; // { a: 2 }
const revertInfo = result.revert;
// Revert the change
const revertedResult = Patch.revert(patchedDocument, revertInfo);
const revertedDocument = revertedResult.doc; // { a: 1 }
Patch.buildRevertPatch(revert)Builds a standard JSON Patch from a revert object. This allows for more flexibility, such as storing the revert patch, applying it later, or combining it with other patches.
revert: The revert object.Returns a valid JSON Patch (an array of operation objects).
import { Patch } from '@api-client/json';
// ... previous example ...
const revertPatch = Patch.buildRevertPatch(revertInfo);
// Now revertPatch is a regular JSON Patch you can store or apply later
const revertedResult = Patch.apply(patchedDocument, revertPatch);
const revertedDocument = revertedResult.doc; // { a: 1 }
Patch.diff(value1, value2)Computes the difference between two JSON values and returns it as a JSON Patch.
value1: The original value.value2: The new value.Returns a JSON Patch (an array of operation objects).
import { Patch } from '@api-client/json';
const original = { a: 1, b: 2 };
const updated = { a: 1, b: 3 };
const diffPatch = Patch.diff(original, updated);
console.log(diffPatch) // it will print: [{ op: 'replace', path: '/b', value: 3 }];
Patch.valid(patch)Checks if a patch is semantically valid according to the JSON Patch specification. It does not check if the patch is valid JSON. Use JSON.parse or a similar method for JSON validation.
patch: The patch to validate.Returns true if the patch is valid, false otherwise.
Patch.add, Patch.remove, Patch.replace, Patch.move, Patch.copy, Patch.test)These methods provide direct access to individual JSON Patch operations. They all have the same signature:
Patch.operation(document, path, value / from) // value for add, replace, test; from for move, copy
document: The JSON document.path: The target path (JSON Pointer).value: Value for add, replace, test.from: Path to the original value for move, copy.They return an object with doc (the modified document) and previous (the previous value at the path, if applicable) properties.
import { Patch } from '@api-client/json';
let myDocument = { foo: 'bar', list: [1,2,3] };
myDocument = Patch.add(myDocument, '/newKey', 'newValue').doc; // Adds newKey:newValue
myDocument = Patch.remove(myDocument, '/foo').doc; // Removes 'foo'
myDocument = Patch.replace(myDocument, '/list/0', 10).doc; //Replaces list[0] with 10
myDocument = Patch.move(myDocument, '/list/1', '/newPath').doc; // Moves list[1] to newPath
myDocument = Patch.copy(myDocument, '/list/0', '/copiedPath').doc; // Copies list[0] to copiedPath
const testResult = Patch.test(myDocument, '/list/0', 10) //returns {doc:myDocument, success: true} // Tests if list[0] equals 10. Doesn't modify the document
Patch.get, Patch.has)Utility functions for interacting with JSON documents.
Patch.get(document, path): Retrieves the value at the specified path.Patch.has(document, path): Checks if a value exists at the specified path.Patch.pack(patch) and Patch.unpack(packedPatch)These methods compress and decompress JSON Patches to reduce their size, which can be useful for storage or transmission.
Patch.pack(patch): Compresses a patch.Patch.unpack(packedPatch): Decompresses a packed patch.const packed = Patch.pack(patch)
const unpacked = Patch.unpack(packed)
Rebasing is a process of adjusting a local patch (client changes) to account for changes made by the server. It's crucial in collaborative scenarios where multiple clients may be editing the same document concurrently. This library provides two main functions for rebasing: Patch.rebase and Patch.rebaseDocument.
Patch.rebase(base, client, server)The Patch.rebase function is a utility for transforming a client's patch against a server's patch, given a common base document. The base should be a common document for both client and server.
base: The original document.client: The patch created by the client.server: The patch created by the server.Returns an object containing the client (the rebased client patch) and diff (the patch representing the changes that happened during the rebase process).
Usage scenario:
import { Patch } from '@api-client/json';
// base document
let doc = { "foo": "bar" };
// server patch
const serverPatch = [{ op: 'replace', path: '/foo', value: 'server' }];
// client patch
const clientPatch = [{ op: 'replace', path: '/foo', value: 'client' }];
const result = Patch.rebase(doc, clientPatch, serverPatch);
console.log(result.client) // [{ op: 'replace', path: '/foo', value: 'client' }]
console.log(result.diff) // [{ op: 'replace', path: '/foo', value: 'server' }]
Patch.rebaseDocument(document, client, server)The Patch.rebaseDocument function is a helper that does the whole rebase process.
document: The document to rebase.client: The patch created by the client.server: The patch created by the server.Returns an object with:
doc: The modified document.diff: The diff (local patch) generated during the rebase process.client: The rebased client patches.Usage scenario:
import { Patch } from '@api-client/json';
// base document
let doc = { "foo": "bar" };
// server patch
const serverPatch = [{ op: 'replace', path: '/foo', value: 'server' }];
// client patch
const clientPatch = [{ op: 'replace', path: '/foo', value: 'client' }];
const result = Patch.rebaseDocument(doc, clientPatch, serverPatch);
console.log(result.doc) // { foo: 'server' }
console.log(result.client) // [{ op: 'replace', path: '/foo', value: 'client' }]
console.log(result.diff) // [{ op: 'replace', path: '/foo', value: 'server' }]
In this example result.doc will be { foo: 'server' } because in this use case the server made a change, and the client will receive it, so it can update its document. The result.diff will be the server's changes, in this case { op: 'replace', path: '/foo', value: 'server' }. The result.client will be the client changes, in this case { op: 'replace', path: '/foo', value: 'client' }.
This process ensures that both the client and server remain in sync with the latest changes.
FAQs
JSON utility libraries for API Client.
We found that @api-client/json demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.