mrkeyoor.com_
Sat 08 Aug 21:59 UTC
npmUtilsupdated 08 Aug 2026

fastest-stable-stringify

fastest-stable-stringify serializes JSON-like JavaScript values after sorting every object's own enumerable keys lexicographically. Objects with the same data but different insertion order therefore produce the same compact string, which is useful for cache keys, hashes, snapshots, and reproducible files. Arrays keep their order and objects with `toJSON` use it. The package is a tiny dependency-free CommonJS function, but it intentionally offers fewer controls and safety features than newer stable stringifiers.

Verdict

Retain it when its byte-for-byte output is already a compatibility contract. For a new project, safe-stable-stringify is the better default because speed is not worth silent option gaps and brittle values without a current workload benchmark.

API stability4/5The public API has remained one function from value to string throughout the 2.x line, and the 2.0.2 output algorithm is small enough to audit: arrays preserve order, object keys use default lexicographic sort, and toJSON is honored. Stability is weakened by undocumented edge behavior, including top-level undefined becoming a JSON null string, and by packaged comparator examples whose second argument the implementation simply ignores.
Docs2/5The README gives installation, one basic call, and a benchmark table, so the core purpose is immediately clear. It does not specify key ordering, top-level primitive behavior, undefined handling, cycles, BigInt, Map, Set, getters, toJSON, ESM, TypeScript, or the absence of options. The benchmark lacks hardware and runtime context, and users must read 49 lines of source plus tests to learn the real serialization contract.
Maintenance1/5npm reports that 2.0.2 was published on 2018-05-10, GitHub reports the last push on 2023-04-04, and the repository still carries Travis configuration and development dependencies from the Tape 1 and ESLint 4 era. It is not archived or registry-deprecated and has only one open issue and PR combined, but there is no current release or CI evidence for modern Node versions and value types.
Ecosystem3/5The package recorded 3,529,326 downloads in the measured week and its deterministic compact output fits cache keys, hashes, snapshots, and reproducible artifacts without a dependency tree. The integration story ends there: no types, ESM export, options, streaming interface, browser entry, or framework adapters. Its large download count likely includes transitive legacy use, while maintained stable-stringify packages cover a wider set of inputs.

Use it if

  • You already depend on its exact lexicographic output and changing serialized bytes would invalidate caches, signatures, or snapshots
  • Your inputs are acyclic, JSON-compatible objects and you only need compact deterministic output
  • You need a zero-dependency CommonJS stringifier for an older Node application
  • You have benchmarks on your own production-shaped values showing this implementation is materially faster than maintained alternatives
Skip it if

Setup reality

`npm install fastest-stable-stringify` adds a 15,717-byte package with no runtime dependencies, peer dependencies, native builds, credentials, or configuration. The documented interface is CommonJS: `const stringify = require('fastest-stable-stringify')`. ESM applications depend on Node's CommonJS default-import interoperability, and TypeScript projects need a local declaration or a third-party type package because 2.0.2 ships no types. There is exactly one supported argument despite example files in the tarball passing comparator callbacks; the implementation ignores those extra arguments and always sorts keys with JavaScript's default lexicographic `Array#sort`. Output is compact only, with no replacer or spacing option. The first-run surprises are input semantics. Circular references recurse until a RangeError instead of producing a helpful cycle error. BigInt and Symbol values can throw. Map and Set have no enumerable string keys and serialize as `{}` unless converted. Date works through `toJSON`, and any custom `toJSON` method can change the data before sorting. Like native JSON, undefined object properties are omitted, undefined array entries become null, and non-finite numbers become null. Unlike native `JSON.stringify`, a top-level undefined or function becomes the string `null` rather than returning undefined. Getters run during traversal, so serialization is not side-effect-free. The README's speed claim comes from its own undated benchmark output; rerun representative benchmarks on your current Node version before choosing it for performance. Finally, deterministic bytes are only useful if all callers use the same package version and preprocessing rules. Do not label its output canonical JSON in a protocol or use it for cross-language signatures without a formal canonicalization layer.

Patterns

Serialize an object deterministicallystringify-object

const stringify = require('fastest-stable-stringify');

const left = stringify({ b: 2, a: 1 });
const right = stringify({ a: 1, b: 2 });
console.log(left); // {"a":1,"b":2}
console.log(left === right); // true

Object keys are sorted lexicographically at every depth. Array item order is preserved.

Build a cache key from structured inputcreate-cache-key

const stringify = require('fastest-stable-stringify');
const { createHash } = require('node:crypto');

function cacheKey(namespace, input) {
  const bytes = stringify(input);
  const digest = createHash('sha256').update(bytes).digest('hex');
  return `${namespace}:${digest}`;
}

Validate inputs first. Cycles and BigInt throw, while Map and Set collapse to empty objects and can cause unintended key collisions.

Compare JSON-like values independent of key insertion ordercompare-json-values

const stringify = require('fastest-stable-stringify');

function sameJsonValue(a, b) {
  return stringify(a) === stringify(b);
}

console.log(sameJsonValue({ x: 1, y: 2 }, { y: 2, x: 1 })); // true

This is not general deep equality: undefined object fields disappear, NaN becomes null, prototypes are ignored, and toJSON can rewrite a value.

Write a reproducible compact JSON filewrite-reproducible-json

const stringify = require('fastest-stable-stringify');
const { writeFile } = require('node:fs/promises');

async function writeManifest(path, manifest) {
  await writeFile(path, stringify(manifest) + '\n', 'utf8');
}

There is no pretty-print option. Adding a final newline is caller policy and changes hashes compared with the returned string alone.

Sign a controlled JavaScript payloadsign-known-payload

const stringify = require('fastest-stable-stringify');
const { createHmac } = require('node:crypto');

function sign(payload, secret) {
  return createHmac('sha256', secret)
    .update(stringify(payload), 'utf8')
    .digest('base64url');
}

Only use this when signer and verifier share the exact JavaScript serializer contract. It is not RFC 8785 canonical JSON for cross-language protocols.

Serialize Date values through toJSONserialize-date

const stringify = require('fastest-stable-stringify');

const value = stringify({
  createdAt: new Date('2026-08-08T12:00:00.000Z'),
});
// {"createdAt":"2026-08-08T12:00:00.000Z"}

The serializer calls any object's toJSON method before sorting. User-defined toJSON can execute code or return unexpected shapes.

Convert a Map before serializationconvert-map

const stringify = require('fastest-stable-stringify');

const headers = new Map([
  ['content-type', 'application/json'],
  ['accept', 'application/json'],
]);
const json = stringify(Object.fromEntries(headers));

Map and Set instances have no enumerable string keys and otherwise serialize as `{}`. Decide how non-string Map keys should be represented.

Convert BigInt values explicitlyencode-bigint

const stringify = require('fastest-stable-stringify');

const record = { id: 9007199254740993n, count: 2n };
const jsonReady = Object.fromEntries(
  Object.entries(record).map(([key, value]) => [
    key,
    typeof value === 'bigint' ? value.toString() : value,
  ]),
);
const json = stringify(jsonReady);

BigInt throws in version 2.0.2. A shallow conversion like this does not handle nested values; use a recursive schema-aware conversion there.

Detect cycles before calling the serializerreject-circular-input

const stringify = require('fastest-stable-stringify');

function assertAcyclic(value, seen = new WeakSet()) {
  if (!value || typeof value !== 'object') return;
  if (seen.has(value)) throw new TypeError('Circular value');
  seen.add(value);
  for (const child of Object.values(value)) assertAcyclic(child, seen);
  seen.delete(value);
}

assertAcyclic(input);
const json = stringify(input);

The package has no cycle option and eventually throws a maximum-call-stack RangeError. This helper permits repeated non-cyclic references.

Make undefined handling explicitpreserve-undefined-policy

const stringify = require('fastest-stable-stringify');

const objectJson = stringify({ present: 1, missing: undefined });
const arrayJson = stringify([1, undefined]);
console.log(objectJson); // {"present":1}
console.log(arrayJson);  // [1,null]

Top-level undefined is different again: this package returns the string `null`, while native JSON.stringify returns undefined.

Load the CommonJS package from Node ESMimport-from-esm

import stringify from 'fastest-stable-stringify';

const value = stringify({ z: 3, a: 1 });
console.log(value);

This relies on Node's CommonJS default-import interop. The package has no ESM build or exports map, so confirm behavior in your bundler.

Add a minimal local TypeScript declarationdeclare-types-locally

// fastest-stable-stringify.d.ts
declare module 'fastest-stable-stringify' {
  function stringify(value: unknown): string;
  export = stringify;
}

The package ships no declarations. The return type is always string in this implementation, including the surprising top-level undefined case.

Alternatives

PackageRegistryPick it when
safe-stable-stringifynpmYou need maintained deterministic serialization with BigInt, circular-value handling, replacers, and configuration
fast-json-stable-stringifynpmYou want the widely used small implementation with a documented comparator and optional cycle handling
json-stable-stringifynpmYou need custom comparison and replacer functions and accept a somewhat larger implementation