JSON cheat sheet
A scannable JSON reference: 31 short snippets across 12 topics, each linking back to the lesson it came from.
At a glance
| Topic | What it covers | |
|---|---|---|
| Syntax and types | The complete JSON grammar, the values that look valid but are not, and how JSON compares to a JavaScript object literal | lesson |
| parse, stringify, revivers and replacers | Convert between text and data safely, transform values during parsing, and know exactly which JavaScript values cannot | lesson |
| JSON in HTTP APIs | Response envelopes, a stable error shape, timestamps as strings, and the error messages you will meet most often | lesson |
| Reading and inspecting JSON from the command line | Several tools accept a relaxed dialect for configuration. They are not JSON, and a parser that complies with the | lesson |
| Designing a JSON payload | Naming, null versus omitted, envelopes, pagination and error contracts - the decisions that are cheap now and expensive | lesson |
| Dates, numbers and precision | Represent time unambiguously, keep money out of floating point, and know exactly when a JSON number stops being the | lesson |
| Validating with JSON Schema | Describe the payload you accept, validate it at the boundary, compose schemas with references and conditionals, and | lesson |
| JSON Lines, streaming and large documents | Use NDJSON for event and log data, parse it line by line, and process files too large to load without running out of | lesson |
| JSON in databases and storage | Query and index JSON columns in PostgreSQL and MySQL, work with nested documents in MongoDB, and recognise when | lesson |
| Security when parsing untrusted JSON | Prototype pollution, size and depth limits, schema validation as an actual control, and keeping JSON safe where it is | lesson |
| When to use something else | YAML, TOML, CSV, MessagePack and Protobuf each win somewhere JSON does not - and each has a cost worth naming before | lesson |
| Testing and mocking JSON APIs | Build fixtures and factories, generate data from a schema, mock at the network boundary, and stop snapshots from making | lesson |
Quick snippets
Syntax and types
The whole grammar
{
"id": 42,
"name": "Ada",
"active": true,
"score": 9.5,
"tags": ["math", "code"],
"address": { "city": "London", "postcode": "N1 9GU" },
"manager": null
}
Rules that trip people up
{
"line": "first\nsecond",
"path": "C:\\Users\\ada",
"quote": "she said \"hello\"",
"char": "\u00e9"
}
JSON versus a JavaScript object literal
// the same data written as a JavaScript object literal
const cfg = {
name: 'app', // comments and single quotes are fine here
retries: undefined, // valid JavaScript, invalid JSON
tags: ['a', 'b',], // trailing comma allowed
};
// converting between the two representations
const text = JSON.stringify(cfg); // {"name":"app","tags":["a","b"]}
const back = JSON.parse(text); // plain data, no functions, no undefinedFull lesson: Syntax and types →
parse, stringify, revivers and replacers
Revivers: transforming during parse
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
const dto = JSON.parse(text, (key, value) => {
if (typeof value === 'string' && iso.test(value)) return new Date(value);
if (key === 'total') return Number(value) || 0;
if (key === '__proto__') return undefined; // drop prototype-polluting keys
return value;
});
dto.createdAt instanceof Date; // trueFull lesson: parse, stringify, revivers and replacers →
JSON in HTTP APIs
Choosing a response shape
{
"data": [
{ "id": 1, "title": "First", "dueAt": "2026-09-20T09:00:00Z" },
{ "id": 2, "title": "Second", "dueAt": null }
],
"meta": { "page": 1, "perPage": 20, "total": 137 }
}
A stable error shape
{
"error": {
"code": "validation_failed",
"message": "The request body is not valid.",
"details": [
{ "field": "email", "issue": "must contain @" }
]
}
}
Practical pitfalls
const res = await fetch('/api/tasks', { headers: { Accept: 'application/json' } });
// a 200 does not mean the body is JSON
const type = res.headers.get('content-type') || '';
if (!type.includes('json')) {
throw new Error('expected JSON, got ' + type);
}
const payload = await res.json();
// dates always arrive as strings and must be converted by the client
const dueAt = payload.data[0].dueAt ? new Date(payload.data[0].dueAt) : null;Full lesson: JSON in HTTP APIs →
Reading and inspecting JSON from the command line
Fetch and format
# what did the server actually send?
curl -sI https://api.example.com/orders | grep -i content-type
# and what does the error look like when the request is wrong?
curl -s https://api.example.com/orders/999 | jq .
JSONC and JSON5 config files
// a JSONC file: valid for the editor, invalid for JSON.parse
{
// the compiler options that matter for this package
"compilerOptions": {
"strict": true,
"noEmit": true,
},
}
// reading it in code needs a tolerant parser
import { parse } from "jsonc-parser";
const config = parse(await readFile("tsconfig.json", "utf8"));
JSONC and JSON5 config files
# strip comments and trailing commas before sending it anywhere
npx strip-json-comments tsconfig.json | jq . > /tmp/clean.json
# JSON5 from the command line
npx json5 -s config.json5Full lesson: Reading and inspecting JSON from the command line →
Designing a JSON payload
Names, nulls and shape
{
"id": "ord_9f2c",
"customer_id": "cus_1042",
"line_items": [
{ "sku": "A-1", "quantity": 2, "unit_price_minor": 1999 }
],
"total_minor": 3998,
"currency": "GBP",
"placed_at": "2026-09-18T11:04:22Z",
"cancelled_at": null,
"coupon": null
}
Envelopes and pagination
{
"data": [
{ "id": "ord_9f2c" },
{ "id": "ord_9f30" }
],
"meta": {
"request_id": "req_7c1a",
"next_cursor": "eyJpZCI6Im9yZF85ZjMwIn0",
"has_more": true
}
}
Envelopes and pagination
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Quantity must be at least 1.",
"details": [
{ "field": "line_items[0].quantity", "issue": "minimum", "limit": 1 }
],
"request_id": "req_7c1a"
}
}Full lesson: Designing a JSON payload →
Dates, numbers and precision
Time as a string
{
"placed_at": "2026-09-18T11:04:22Z",
"placed_at_offset": "2026-09-18T12:04:22+01:00",
"delivery_date": "2026-09-21",
"trial_ends_epoch_ms": 1790000000000
}
Time as a string
// the two mistakes that show up in production
JSON.parse('{"d":"2026-09-18 11:04:22"}').d // no zone: parsed as local time
new Date("2026-09-18").toISOString() // "2026-09-18T00:00:00.000Z" in UTC,
// but the previous day in a negative offset
// a date-only value is not an instant - keep it a string
const deliveryDate = "2026-09-21";
Number.isFinite(Date.parse(deliveryDate)); // true, but the instant is zone-dependent
Numbers that survive the trip
Number.MAX_SAFE_INTEGER; // 9007199254740991
Number.MAX_SAFE_INTEGER + 1; // 9007199254740992
Number.MAX_SAFE_INTEGER + 2; // 9007199254740992 - the same number
// a 64-bit identifier, as it round-trips through JSON
const id = 9007199254740993n;
JSON.parse('{"id":9007199254740993}').id; // 9007199254740992 - silently wrong
// floats are binary, so decimals are approximate
0.1 + 0.2; // 0.30000000000000004
JSON.parse('{"total":10.1}').total * 3; // 30.299999999999997Full lesson: Dates, numbers and precision →
Validating with JSON Schema
One schema, several outputs
# TypeScript types from the schema
npx json-schema-to-typescript schemas/order.json -o src/types/order.d.ts
# Python models
datamodel-codegen --input schemas/order.json --output models/order.py
# generate a valid example, useful for fixtures and docs
npx json-schema-faker schemas/order.json > fixtures/order.json
# documentation, if your toolchain renders it
npx @adobe/jsonschema2md -d schemas -o docs/schemas
One schema, several outputs
test("rejects an order with no line items", () => {
expect(validate({ id: "ord_1", total_minor: 0, currency: "GBP", line_items: [] })).toBe(false);
});
test("rejects an unknown property", () => {
expect(validate({ id: "ord_1", total_minor: 0, currency: "GBP", extra: 1 })).toBe(false);
});Full lesson: Validating with JSON Schema →
JSON Lines, streaming and large documents
One object per line
{"ts":"2026-09-18T11:00:00Z","level":"info","msg":"started","pid":412}
{"ts":"2026-09-18T11:00:01Z","level":"warn","msg":"retry","attempt":1}
{"ts":"2026-09-18T11:00:04Z","level":"info","msg":"ready","port":3000}
Files that do not fit in memory
// this loads the whole file, then doubles it while parsing
const all = JSON.parse(await readFile("big.json", "utf8")); // 2 GB file, 4 GB+ of RAM
// line-oriented processing stays flat
const rl = createInterface({ input: createReadStream("big.ndjson") });
let count = 0;
for await (const line of rl) {
if (!line.trim()) continue;
if (JSON.parse(line).level === "error") count++;
}
console.log(count);
Files that do not fit in memory
# inspect a large NDJSON file without loading it all
head -c 500 big.ndjson
wc -l big.ndjson
# filter and count with jq, streaming
jq -c 'select(.level == "error")' big.ndjson | wc -l
# sample the first 20 records
head -20 big.ndjson | jq -s .Full lesson: JSON Lines, streaming and large documents →
JSON in databases and storage
When to normalise instead
-- a schema in practice, expressed as a constraint
ALTER TABLE events ADD CONSTRAINT payload_shape CHECK (
jsonb_typeof(payload) = 'object'
AND payload ? 'level'
AND jsonb_typeof(payload -> 'level') = 'string'
);Full lesson: JSON in databases and storage →
Security when parsing untrusted JSON
Size, depth and cost
# what does your endpoint do with a 10 MB nested body?
python - <<'PY' > /tmp/deep.json
depth = 20000
print('{"a":' * depth + '1' + '}' * depth)
PY
curl -s -o /dev/null -w '%{http_code} %{size_upload}\n' \
-X POST http://localhost:3000/api/events \
-H 'Content-Type: application/json' \
--data-binary @/tmp/deep.json
JSON embedded in HTML and logs
// a payload written into a page must not be able to close the containing block
function safeForInlineJson(value) {
return JSON.stringify(value)
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e")
.replace(/&/g, "\\u0026")
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
}
// escaping the opening angle bracket is what stops a string value
// from terminating the surrounding HTML element
JSON embedded in HTML and logs
// redaction before anything is written to a log
const SECRET = /^(password|token|authorization|card_number|cvv)$/i;
function redact(value) {
if (Array.isArray(value)) return value.map(redact);
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [k, SECRET.test(k) ? "[redacted]" : redact(v)])
);
}
return value;
}Full lesson: Security when parsing untrusted JSON →
When to use something else
Configuration: YAML and TOML
# YAML: comments, anchors, and significant indentation
defaults: &defaults
retries: 3
timeout_seconds: 30
services:
api:
<<: *defaults
port: 8080
hosts:
- api.example.com
- api.internal
Configuration: YAML and TOML
# TOML: explicit, typed, and unambiguous about nesting
[service]
name = "api"
port = 8080
[service.limits]
retries = 3
timeout_seconds = 30
enabled = true
released = 2026-09-18 # a real date type, not a string
Tabular data: CSV
id,email,plan,created_at
1042,[email protected],annual,2026-01-04
1043,[email protected],monthly,2026-02-11Full lesson: When to use something else →
Testing and mocking JSON APIs
Fixtures and factories
// fixtures/order.json - a real recorded response, trimmed
{
"data": {
"id": "ord_9f2c",
"total_minor": 3998,
"currency": "GBP",
"status": "placed",
"placed_at": "2026-09-18T11:04:22Z",
"line_items": [{ "sku": "A-1", "quantity": 2 }]
},
"meta": { "request_id": "req_7c1a" }
}
Generated data and schema checks
# random-but-valid data from the schema
npx json-schema-faker schemas/order.json --count 5 > fixtures/orders.random.json
# a property-style check on the parser itself
npx fast-check --example
Mocking at the boundary
// a snapshot that fails for the right reasons
expect(anOrder().data).toMatchObject({
currency: "GBP",
status: "placed",
line_items: [{ sku: "A-1", quantity: 2 }],
});Full lesson: Testing and mocking JSON APIs →
FAQ
Is this JSON cheat sheet free to use?
Where do the examples come from?
How do I go deeper than a cheat sheet?
Related cheat sheets
HTML CSS JavaScript TypeScript HTML DOM AJAX
Last refreshed 2026-09-27.