PrimeBrickPrimeBrick
  • Docs
  • Contact
  • MIT License
  • Documentation
  • MCP Server
  • API Catalog
  • Services
  • Libraries
PrimeBrickPrimeBrick

© 2026 PrimeBrick. MIT License. v3.11.2

github
DAL Library
SDK Library
    Developer Guide
      OverviewGetting StartedAnatomy of a MicroserviceAuthenticationClient Shield & Rotating SecretsHTTP ServerRPC RouterEntity RouterOpenAPI GenerationDocs PackConfig tables & ConfigLoaderDatabase MigrationsTranslationsCurrency UtilitiesNATS™ ClientService RegistrationSSE StandardPresenceExt-JSONRedis cache layerTelemetry & LoggingAPI Reference
Decimal Library
powered by Zudoku
Developer Guide

Ext-JSON

Ext-JSON is the standard JSON serialization layer for Primebrick backend services. It uses json-bigint with useNativeBigInt: true to preserve bigint values across the JSON wire format.

Why Ext-JSON exists

Standard JSON.stringify() throws on bigint:

Code
JSON.stringify({ id: 42n }); // TypeError: Do not know how to serialize a BigInt

Primebrick uses native bigint for all integer IDs (UUIDs are strings, but numeric IDs, timestamps, and counters are bigint). Ext-JSON makes the types predictable across the wire format:

JSON valueWire formatParsed TS type
42n (bigint)42bigint
42 (small int)42bigint (reviver forces it)
3.14 (float)3.14number
1e5 (scientific)100000bigint (integer)
"alice" (string)"alice"string
true (boolean)trueboolean
nullnullnull

The rule is simple: every integer is bigint, every float is number. No number | bigint ambiguity — the type is determined by whether the value has a decimal point, not by its magnitude.

The reviver

extJsonParse uses a reviver that forces every integer to bigint:

Code
extJsonParse(text, (_key, value) => { if (typeof value === "number" && Number.isInteger(value)) { return BigInt(value); } return value; });

json-bigint's alwaysParseAsBig option would do this, but it is broken for floats in v1.0.0 — so the SDK uses a reviver instead. The reviver is the reason 42 (a small integer that json-bigint returns as number) becomes bigint on the receiving end, matching the 42n the sender wrote.

Usage

Direct functions

Code
import { extJsonStringify, extJsonParse } from "@primebrick/sdk"; const data = { entity_id: 42n, price: 3.14, name: "widget" }; const json = extJsonStringify(data); // → '{"entity_id":42,"price":3.14,"name":"widget"}' const parsed = extJsonParse<{ entity_id: bigint; price: number; name: string }>(json); // parsed.entity_id → 42n (bigint) // parsed.price → 3.14 (number)

Round-trip type preservation

The round-trip preserves types exactly — bigint in, bigint out; number in, number out. This is what makes the SDK's NATS™ and HTTP layers safe for DAL rows that mix bigint IDs with number floats:

Code
const original = { id: 42n, // bigint → bigint count: 7, // number (integer) → bigint (reviver forces it) price: 3.14, // number (float) → number name: "widget", // string → string active: true, // boolean → boolean nested: { qty: 100n },// bigint nested → bigint }; const roundTrip = extJsonParse<typeof original>(extJsonStringify(original)); // typeof roundTrip.id → "bigint" // typeof roundTrip.count → "bigint" (was a number, now bigint — by design) // typeof roundTrip.price → "number" // typeof roundTrip.name → "string" // typeof roundTrip.nested.qty → "bigint"

The one asymmetry to know about: a plain number integer on the sender becomes bigint on the receiver. This is intentional — it makes every integer bigint everywhere, so consumers never write number | bigint union types. If you send a number integer, expect a bigint back.

Express middleware (BE)

Code
import { extJsonMiddleware, extJsonBodyParser } from "@primebrick/sdk"; // Parse request bodies with BigInt-safe JSON (replaces express.json()) app.use(extJsonBodyParser()); // Serialize responses with Ext-JSON app.use(extJsonMiddleware()); // res.json() now uses Ext-JSON serialization for all subsequent responses

The middleware replaces res.json() with Ext-JSON serialization. The body parser replaces express.json() with BigInt-safe parsing so that large integer values in request bodies (e.g. bigint config values) are parsed as native bigint instead of losing precision through JSON.parse. Install both once before any routes.

NATS™ (microservices)

NatsClient.publish(), subscribe(), and subscribeRequest() use Ext-JSON internally. Microservice code never calls extJsonStringify / extJsonParse directly — just pass plain objects and receive plain objects.

Sub-path import

If you need only the JSON functions without pulling in NATS™/auth code paths:

Code
import { extJsonStringify, extJsonParse, extJsonMiddleware, extJsonBodyParser } from "@primebrick/sdk/json";

Not for the frontend

The frontend has its own standalone wrapper (src/lib/api-ext.ts) that installs json-bigint directly. The FE does not depend on @primebrick/sdk. Ext-JSON is for BE and microservices only.

Next steps

  • NATS Client — uses Ext-JSON internally
  • HTTP Server — uses Ext-JSON for health and error responses
  • API Reference — extJsonStringify, extJsonParse, extJsonMiddleware, extJsonBodyParser
Last modified on October 28, 2026
PresenceRedis cache layer
On this page
  • Why Ext-JSON exists
  • The reviver
  • Usage
    • Direct functions
    • Round-trip type preservation
    • Express middleware (BE)
    • NATS™ (microservices)
  • Sub-path import
  • Not for the frontend
  • Next steps
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript
TypeScript