CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-bson

A BSON (Binary JSON) parser for Node.js and browsers with comprehensive data type support and Extended JSON functionality

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

extended-json.mddocs/

Extended JSON (EJSON)

Extended JSON functionality for converting BSON data to/from human-readable JSON format while preserving type information and ensuring round-trip fidelity.

Capabilities

EJSON Parse

Parse Extended JSON string to JavaScript/BSON objects with type preservation.

/**
 * Parse Extended JSON string to JavaScript/BSON objects
 * @param text - Extended JSON string to parse
 * @param options - Optional parsing configuration
 * @returns Parsed JavaScript object with BSON types
 */
function parse(text: string, options?: EJSONOptions): any;

interface EJSONOptions {
  /** Output using Extended JSON v1 spec (default: false) */
  legacy?: boolean;
  /** Return native JS types when possible rather than BSON types (default: true) */
  relaxed?: boolean;
  /** Use native BigInt for 64-bit integers (default: false) */
  useBigInt64?: boolean;
}

Usage Examples:

import { EJSON } from "bson";

// Parse with BSON types preserved
const ejsonString = '{ "id": { "$oid": "507f1f77bcf86cd799439011" }, "count": { "$numberLong": "42" } }';
const strict = EJSON.parse(ejsonString, { relaxed: false });
console.log(strict.id); // ObjectId instance
console.log(strict.count); // Long instance

// Parse with relaxed mode (native JS types when possible)
const relaxed = EJSON.parse(ejsonString, { relaxed: true });
console.log(relaxed.id); // ObjectId instance (can't convert to string)
console.log(relaxed.count); // 42 (converted to number)

// Parse with BigInt support
const bigIntString = '{ "bigValue": { "$numberLong": "9223372036854775807" } }';
const withBigInt = EJSON.parse(bigIntString, { useBigInt64: true });
console.log(typeof withBigInt.bigValue); // "bigint"

EJSON Stringify

Convert JavaScript/BSON objects to Extended JSON string format.

/**
 * Convert JavaScript/BSON objects to Extended JSON string
 * @param value - Object to convert to Extended JSON
 * @param replacer - Function or array to transform values (like JSON.stringify)
 * @param space - Indentation for pretty-printing (like JSON.stringify)
 * @param options - Optional stringification configuration
 * @returns Extended JSON string representation
 */
function stringify(
  value: any,
  replacer?: Function | Array,
  space?: string | number,
  options?: EJSONOptions
): string;

Usage Examples:

import { EJSON, ObjectId, Long } from "bson";

const document = {
  _id: new ObjectId(),
  count: Long.fromNumber(42),
  timestamp: new Date(),
  binary: new Uint8Array([1, 2, 3, 4])
};

// Strict Extended JSON with type preservation
const strict = EJSON.stringify(document, undefined, 2, { relaxed: false });
console.log(strict);
// {
//   "_id": { "$oid": "..." },
//   "count": { "$numberLong": "42" },
//   "timestamp": { "$date": "2023-..." },
//   "binary": { "$binary": { "base64": "AQIDBA==", "subType": "00" } }
// }

// Relaxed Extended JSON (more readable)
const relaxed = EJSON.stringify(document, undefined, 2, { relaxed: true });
console.log(relaxed);
// {
//   "_id": { "$oid": "..." },
//   "count": 42,
//   "timestamp": "2023-...",
//   "binary": { "$binary": { "base64": "AQIDBA==", "subType": "00" } }
// }

// With custom replacer function
const filtered = EJSON.stringify(document, (key, value) => {
  if (key === 'binary') return undefined; // Exclude binary data
  return value;
});

EJSON Serialize

Serialize BSON objects to Extended JSON object representation (not string).

/**
 * Serialize BSON objects to Extended JSON object representation
 * @param bson - BSON object to serialize
 * @param options - Optional serialization configuration
 * @returns Extended JSON object (not string)
 */
function serialize(bson: any, options?: EJSONOptions): any;

Usage Examples:

import { EJSON, ObjectId, Decimal128 } from "bson";

const bsonDoc = {
  _id: new ObjectId(),
  price: Decimal128.fromString("19.99"),
  active: true
};

// Serialize to Extended JSON object
const ejsonObj = EJSON.serialize(bsonDoc, { relaxed: false });
console.log(ejsonObj);
// {
//   _id: { $oid: "..." },
//   price: { $numberDecimal: "19.99" },
//   active: true
// }

// Can be used with regular JSON.stringify for final string conversion
const jsonString = JSON.stringify(ejsonObj, null, 2);

EJSON Deserialize

Deserialize Extended JSON object back to JavaScript/BSON objects.

/**
 * Deserialize Extended JSON object to JavaScript/BSON objects
 * @param ejson - Extended JSON object to deserialize
 * @param options - Optional deserialization configuration
 * @returns JavaScript object with BSON types
 */
function deserialize(ejson: any, options?: EJSONOptions): any;

Usage Examples:

import { EJSON } from "bson";

// Extended JSON object (not string)
const ejsonObj = {
  _id: { $oid: "507f1f77bcf86cd799439011" },
  count: { $numberLong: "42" },
  price: { $numberDecimal: "19.99" },
  timestamp: { $date: "2023-10-15T10:30:00.000Z" }
};

// Deserialize to BSON objects
const bsonDoc = EJSON.deserialize(ejsonObj, { relaxed: false });
console.log(bsonDoc._id); // ObjectId instance
console.log(bsonDoc.count); // Long instance
console.log(bsonDoc.price); // Decimal128 instance
console.log(bsonDoc.timestamp); // Date instance

// Deserialize with relaxed mode
const relaxedDoc = EJSON.deserialize(ejsonObj, { relaxed: true });
console.log(typeof relaxedDoc.count); // "number" (if within safe range)

Extended JSON Format Specification

Extended JSON provides a way to represent BSON types in JSON format. There are two modes:

Strict Mode (relaxed: false)

All BSON types are explicitly marked with $type indicators:

{
  "_id": { "$oid": "507f1f77bcf86cd799439011" },
  "count": { "$numberLong": "42" },
  "price": { "$numberDecimal": "19.99" },
  "timestamp": { "$date": "2023-10-15T10:30:00.000Z" },
  "binary": { "$binary": { "base64": "AQIDBA==", "subType": "00" } },
  "regex": { "$regularExpression": { "pattern": "^test", "options": "i" } }
}

Relaxed Mode (relaxed: true)

Native JSON types are used when possible for better readability:

{
  "_id": { "$oid": "507f1f77bcf86cd799439011" },
  "count": 42,
  "price": { "$numberDecimal": "19.99" },
  "timestamp": "2023-10-15T10:30:00.000Z",
  "binary": { "$binary": { "base64": "AQIDBA==", "subType": "00" } },
  "regex": "/^test/i"
}

Type Mappings

BSON to Extended JSON

// ObjectId
new ObjectId() → { "$oid": "507f1f77bcf86cd799439011" }

// Long (strict mode)
Long.fromNumber(42) → { "$numberLong": "42" }

// Long (relaxed mode, when safe)
Long.fromNumber(42) → 42

// Decimal128
Decimal128.fromString("19.99") → { "$numberDecimal": "19.99" }

// Binary
new Binary([1,2,3,4]) → { "$binary": { "base64": "AQIDBA==", "subType": "00" } }

// Date (strict mode)
new Date() → { "$date": "2023-10-15T10:30:00.000Z" }

// Date (relaxed mode)
new Date() → "2023-10-15T10:30:00.000Z"

// RegExp (strict mode)
/pattern/flags → { "$regularExpression": { "pattern": "pattern", "options": "flags" } }

// RegExp (relaxed mode)
/pattern/flags → "/pattern/flags"

// Timestamp
new Timestamp(1, 1697365800) → { "$timestamp": { "t": 1697365800, "i": 1 } }

// MinKey/MaxKey
new MinKey() → { "$minKey": 1 }
new MaxKey() → { "$maxKey": 1 }

Round-Trip Fidelity

Extended JSON ensures perfect round-trip conversion:

import { EJSON, serialize, deserialize } from "bson";

const original = {
  _id: new ObjectId(),
  bigNumber: Long.fromString("9223372036854775807"),
  precise: Decimal128.fromString("123.456789012345678901234567890")
};

// BSON → Extended JSON → BSON
const ejsonString = EJSON.stringify(original, undefined, undefined, { relaxed: false });
const restored = EJSON.parse(ejsonString, { relaxed: false });

// Should be identical to original
console.log(original._id.equals(restored._id)); // true
console.log(original.bigNumber.equals(restored.bigNumber)); // true
console.log(original.precise.toString() === restored.precise.toString()); // true

// Also works with binary serialization
const bsonBytes = serialize(original);
const fromBson = deserialize(bsonBytes);
const ejsonFromBson = EJSON.stringify(fromBson, undefined, undefined, { relaxed: false });
const backToBson = EJSON.parse(ejsonFromBson, { relaxed: false });

// Perfect round-trip fidelity
console.log(original._id.equals(backToBson._id)); // true

Legacy Format Support

Extended JSON v1 format is supported for backwards compatibility:

import { EJSON } from "bson";

const document = { count: Long.fromNumber(42) };

// Extended JSON v2 (default)
const v2 = EJSON.stringify(document);
// { "count": { "$numberLong": "42" } }

// Extended JSON v1 (legacy)
const v1 = EJSON.stringify(document, undefined, undefined, { legacy: true });
// { "count": { "$numberLong": 42 } } // Note: number instead of string

Install with Tessl CLI

npx tessl i tessl/npm-bson

docs

data-types.md

experimental.md

extended-json.md

index.md

serialization.md

utilities.md

tile.json