fast-json-stringify, json-stable-stringify, and json-stringify-safe are specialized utilities that extend or replace the native JSON.stringify method to solve specific problems like speed, key ordering, and circular references. fast-json-stringify compiles a schema into a high-performance serialization function, ideal for APIs with known data structures. json-stable-stringify ensures deterministic output by sorting keys, which is critical for hashing or caching. json-stringify-safe prevents crashes when encountering circular references, making it suitable for logging complex objects.
Native JSON.stringify is fast but has limitations — it throws on circular references and does not guarantee key order across engines. The packages fast-json-stringify, json-stable-stringify, and json-stringify-safe address these gaps with different trade-offs. Let's compare how they handle serialization in real-world scenarios.
fast-json-stringify compiles a JSON Schema into a dedicated JavaScript function.
// fast-json-stringify: Compile schema once
const fastJson = require('fast-json-stringify');
const stringify = fastJson({
title: 'User',
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' }
}
});
const result = stringify({ id: 1, name: 'Alice' });
// Output: "{\"id\":1,\"name\":\"Alice\"}"
json-stable-stringify walks the object tree recursively at runtime.
// json-stable-stringify: Sort keys recursively
const stringify = require('json-stable-stringify');
const obj = { name: 'Alice', id: 1 };
const result = stringify(obj);
// Output: "{\"id\":1,\"name\":\"Alice\"}" (keys sorted)
json-stringify-safe wraps the native stringify logic with safety checks.
// json-stringify-safe: Safe walk with cycle detection
const stringify = require('json-stringify-safe');
const obj = { name: 'Alice' };
obj.self = obj; // Circular reference
const result = stringify(obj);
// Output: "{\"name\":\"Alice\",\"self\":\"[Circular]\"}"
Circular references are common in logging but invalid in standard JSON. Each package handles this differently.
fast-json-stringify does not support circular references by default.
// fast-json-stringify: Will fail on circular refs
const obj = { name: 'Alice' };
obj.self = obj;
try {
const result = stringify(obj); // Throws or invalid
} catch (e) {
console.error('Serialization failed');
}
json-stable-stringify also fails on circular references.
// json-stable-stringify: Throws on circular refs
const obj = { name: 'Alice' };
obj.self = obj;
try {
const result = stringify(obj); // Throws RangeError
} catch (e) {
console.error('Circular reference detected');
}
json-stringify-safe is built specifically to handle this case.
'[Circular]'.// json-stringify-safe: Handles circular refs gracefully
const obj = { name: 'Alice' };
obj.self = obj;
const result = stringify(obj, null, 2, function(key, value) {
return '[Removed]'; // Custom decycler
});
// Output: { "name": "Alice", "self": "[Removed]" }
Key order matters when generating hashes or comparing snapshots.
fast-json-stringify follows the schema property order.
// fast-json-stringify: Order defined by schema
const schema = {
type: 'object',
properties: {
z: { type: 'string' },
a: { type: 'string' }
}
};
// Output will always be {"z":..., "a":...} regardless of input order
json-stable-stringify sorts keys alphabetically by default.
// json-stable-stringify: Alphabetical sort
const obj = { z: 1, a: 2 };
const result = stringify(obj);
// Output: "{\"a\":2,\"z\":1}"
json-stringify-safe preserves engine-dependent order.
JSON.stringify behavior (usually insertion order).// json-stringify-safe: Native order preserved
const obj = { z: 1, a: 2 };
const result = stringify(obj);
// Output: "{\"z\":1,\"a\":2}" (order not guaranteed across engines)
Speed is the main differentiator for high-throughput systems.
fast-json-stringify is the fastest option for known schemas.
JSON.stringify.// fast-json-stringify: Optimized for speed
// Compile once, run many times
const stringify = fastJson(schema);
// Serialization happens at native speed
json-stable-stringify is significantly slower than native.
// json-stable-stringify: High overhead
// Do not use inside tight loops
const result = stringify(largeObject);
json-stringify-safe has slight overhead compared to native.
// json-stringify-safe: Moderate overhead
// Safe for logging complex state
const log = stringify(complexState);
| Feature | fast-json-stringify | json-stable-stringify | json-stringify-safe |
|---|---|---|---|
| Primary Goal | ⚡ Maximum Performance | 🔤 Deterministic Key Order | 🛡️ Circular Reference Safety |
| Schema Required | ✅ Yes | ❌ No | ❌ No |
| CircularRefs | ❌ Throws/Fails | ❌ Throws | ✅ Handles Safely |
| Key Order | 📋 Schema Definition | 🔠 Alphabetical | 🔄 Native/Insertion |
| Maintenance | 🟢 Active (Fastify) | 🟡 Mature/Stable | 🟡 Mature/Stable |
fast-json-stringify is the top choice for production APIs 🚀 where you control the data shape and need speed. It reduces CPU usage significantly under load. However, it requires upfront schema definition.
json-stable-stringify is essential for security and caching 🔐 where object equality depends on content rather than reference. Use it for generating signatures or cache keys, but keep it away from performance-critical loops.
json-stringify-safe remains the standard for logging 🪵 complex state where circular references might exist. While the package is mature and rarely updated, its logic is simple and reliable for debugging tools. For new projects transmitting data, prefer fixing circular refs at the source instead.
Choose json-stringify-safe when logging or debugging objects that might contain circular references. It prevents your application from crashing by replacing circular links with a placeholder string. It is not suitable for data transmission where valid JSON is required, as the output may contain non-standard placeholders.
Choose fast-json-stringify when you need maximum serialization speed and have a defined JSON Schema for your data. It is best suited for high-throughput API responses where payload structure is consistent and known upfront. Avoid it for logging or debugging unknown objects since it requires strict schema validation.
Choose json-stable-stringify when key order matters, such as generating cryptographic signatures or cache keys based on object content. It is useful for testing equality between objects where property order might vary. Do not use it for performance-critical paths as it is significantly slower than native stringification.
Like JSON.stringify, but doesn't throw on circular references.
Takes the same arguments as JSON.stringify.
var stringify = require('json-stringify-safe');
var circularObj = {};
circularObj.circularRef = circularObj;
circularObj.list = [ circularObj, circularObj ];
console.log(stringify(circularObj, null, 2));
Output:
{
"circularRef": "[Circular]",
"list": [
"[Circular]",
"[Circular]"
]
}
stringify(obj, serializer, indent, decycler)
The first three arguments are the same as to JSON.stringify. The last is an argument that's only used when the object has been seen already.
The default decycler function returns the string '[Circular]'.
If, for example, you pass in function(k,v){} (return nothing) then it
will prune cycles. If you pass in function(k,v){ return {foo: 'bar'}},
then cyclical objects will always be represented as {"foo":"bar"} in
the result.
stringify.getSerialize(serializer, decycler)
Returns a serializer that can be used elsewhere. This is the actual function that's passed to JSON.stringify.
Note that the function returned from getSerialize is stateful for now, so
do not use it more than once.