Module P-13·20 min read

JSON.parse/stringify edge cases, streaming JSON for large payloads, JSON Schema with Ajv, MessagePack as a binary alternative, and the serialization cost at scale.

Module P-13 — JSON Internals, Serialization, and Schema Validation

What this module covers: JSON is everywhere in Node.js — request bodies, response payloads, configuration files, inter-service communication. Most developers treat it as a black box and pay for it later with silent data loss, unexpected type coercions, and performance problems at scale. This module covers what JSON.parse and JSON.stringify actually do under the hood, the edge cases that corrupt data silently, streaming JSON for payloads too large to hold in memory, fast serialization with fast-json-stringify, JSON Schema validation with Ajv, and when MessagePack is worth the switch.


What JSON.parse and JSON.stringify Actually Do

JSON is a text format. JSON.parse converts a string into a JavaScript object. JSON.stringify does the reverse. The key word is string — everything must pass through text.

This matters because JavaScript has types that JSON doesn't:

JavaScript typeJSON resultWhat happens
undefinedomittedproperty disappears
NaN"null"silently becomes null
Infinity"null"silently becomes null
Date"2024-05-22T14:32:01.123Z"becomes a string, not a Date on parse
BigIntthrows TypeErrorcrashes your process
Map, Set{}serialised as empty objects
Symbolomittedproperty disappears
Circular referencethrows TypeErrorcrashes your process
typescript

Safe Patterns

Handling BigInt

BigInt appears frequently when working with PostgreSQL's BIGSERIAL primary keys or blockchain IDs that exceed Number.MAX_SAFE_INTEGER (2⁵³ - 1 ≈ 9 quadrillion):

typescript

Handling Dates

JSON.stringify serialises Dates as ISO strings. JSON.parse gives you strings back. To restore Dates you need a reviver:

typescript

Detecting circular references

typescript

The replacer and reviver Parameters

JSON.stringify(value, replacer, space)replacer controls what gets serialised:

typescript

JSON.parse(text, reviver)reviver transforms values after parsing:

typescript

JSON.stringify Performance at Scale

At low volume, JSON.stringify is invisible. At 10,000 requests/second returning 50-field objects, it becomes measurable.

typescript

fast-json-stringify pre-compiles a serialiser from a JSON Schema — it knows the shape of the output ahead of time and skips runtime introspection:

bash
typescript

Use this on high-traffic endpoints where you know the exact response shape. Not worth the complexity for infrequently called routes.


Streaming JSON for Large Payloads

Loading a 100,000-row query result into memory, calling JSON.stringify, and writing the result is a memory spike waiting to happen. Streaming processes rows as they arrive from the database:

bash
typescript

For Postgres with raw SQL, use pg-query-stream:

bash
typescript

JSON Schema Validation with Ajv

Ajv (Another JSON Schema validator) is the fastest JSON Schema validator for Node.js. JSON Schema is the standard for describing the structure of JSON data — more portable than Zod (which is TypeScript-specific) and used widely in OpenAPI and form validation.

bash
typescript

Ajv vs Zod: Zod infers TypeScript types and is designed for TypeScript projects. Ajv follows the JSON Schema standard and is faster — it compiles schemas to optimised validation functions. For Node.js APIs already using Zod (as in P-4), stick with Zod. Ajv shines when you need JSON Schema portability (shared schemas between frontend/backend) or raw performance.


MessagePack: Binary Alternative to JSON

MessagePack is a binary serialisation format that is 20–50% smaller than JSON and faster to serialise/deserialise. It supports types that JSON doesn't: integers, floats, binary data, timestamps.

When to consider it:

  • High-frequency inter-service communication (microservices)
  • Real-time applications where payload size affects latency
  • Storing structured data in Redis (smaller footprint)
bash
typescript

MessagePack in Express

typescript

For most REST APIs serving browsers, JSON is the right choice — browsers speak JSON natively, the bandwidth difference is negligible, and debuggability is far better. MessagePack is the right choice for Node-to-Node communication where you control both ends and need to squeeze out latency.


The JSON.parse Cost in Hot Paths

JSON.parse parses the entire string synchronously. On the main thread. A 1MB request body takes ~5ms. At 1,000 req/s, that's 5 seconds of JSON parsing per second on one thread.

Strategies for hot paths:

  1. Validate size before parsing — reject oversized bodies early (express.json({ limit: '10kb' }))
  2. Use fast-json-stringify for serialisation on high-throughput routes
  3. Cache parsed results when the same JSON is parsed repeatedly (e.g., static configuration)
  4. Move heavy parsing to a worker thread for CPU-intensive transformations
typescript

Summary

  • undefined, NaN, Infinity, BigInt, Date, Map, Set all behave unexpectedly with JSON.stringify. Know the conversion table.
  • BigInt throws; serialise it as a string. Dates become strings; use a reviver to restore them. Circular references throw; use a replacer with a WeakSet.
  • replacer as an array is the cleanest way to whitelist response fields — prevents sensitive data leaking from the serialisation layer.
  • fast-json-stringify pre-compiles serialisers from schemas — 4× faster than built-in JSON.stringify for high-throughput, fixed-shape responses.
  • Stream JSON for large exports using JSONStream.stringify piped through Prisma/pg stream results — constant memory regardless of result set size.
  • Ajv is fastest for JSON Schema validation; portable across languages and tools. Use it when you need JSON Schema portability. Use Zod when you want TypeScript inference.
  • MessagePack is 20–50% smaller and faster than JSON. Worth it for Node-to-Node communication. Not worth it for browser-facing APIs.

Next: Dockerizing Node.js applications for production — multi-stage builds, Alpine vs slim images, non-root users, health checks, and the production readiness checklist for containerised deployments.


Knowledge Check

When dealing with a large result set from a database in Node.js, what is the best way to return it as JSON to avoid high memory consumption?


Which statement correctly describes the difference between Zod and Ajv in Node.js?


What is a primary reason to consider MessagePack over JSON in a Node.js microservice architecture?

Test your knowledge with more question sets

Sign in to access a wider variety of questions and get notified when new practice sets are added to this module.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.