Skip to main content

JSON Parsing in JavaScript: Safe Patterns and Practical Tips

JSON is the default data format for web APIs, config files, and lightweight storage. But parsing JSON safely is more than calling JSON.parse. You need to consider error handling, validation, types, and serialization so the data you read is trustworthy and predictable. This guide covers the full workflow with TypeScript examples and expected results.

Quick start

const json = '{"id":1,"name":"Ada"}';
const user = JSON.parse(json) as { id: number; name: string };

console.log(user.name);

Result:

Ada

JSON parsing checklist

  • Wrap JSON.parse in try/catch for invalid input.
  • Validate the shape before you use the data.
  • Keep JSON small if you parse frequently.
  • Serialize with stable keys if you compare outputs.
  • Avoid lossy types (like Date or BigInt) without a plan.

Basic parsing with error handling

function safeParse<T>(input: string): { ok: true; value: T } | { ok: false; error: string } {
try {
return { ok: true, value: JSON.parse(input) as T };
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
return { ok: false, error: message };
}
}

const result = safeParse<{ id: number }>('{"id": 1}');
console.log(result);

Result:

{ ok: true, value: { id: 1 } }

Validating the shape

Parsing JSON only gives you any. Always validate before using it.

type User = { id: number; name: string };

function isUser(value: unknown): value is User {
if (typeof value !== "object" || value === null) return false;
const v = value as Record<string, unknown>;
return typeof v.id === "number" && typeof v.name === "string";
}

const parsed = JSON.parse('{"id": 7, "name": "Ada"}') as unknown;
console.log(isUser(parsed));

Result:

true

JSON.stringify basics

Use JSON.stringify for API payloads, cache values, or logging. It only supports JSON-safe types.

const data = { id: 1, name: "Ada", active: true };
const jsonText = JSON.stringify(data);

console.log(jsonText);

Result:

{"id":1,"name":"Ada","active":true}

Pretty printing

For debug logs or config files, format JSON with indentation.

const pretty = JSON.stringify({ id: 1, name: "Ada" }, null, 2);
console.log(pretty);

Result:

{
"id": 1,
"name": "Ada"
}

Reviver for data transformations

The reviver option lets you transform values during parse, such as converting dates.

const input = '{"createdAt":"2026-02-02T14:30:00Z"}';

const withDates = JSON.parse(input, (key, value) => {
if (key === "createdAt" && typeof value === "string") {
return new Date(value);
}
return value;
}) as { createdAt: Date };

console.log(withDates.createdAt.toISOString());

Result:

2026-02-02T14:30:00.000Z

Replacer for controlling output

The replacer option lets you control which keys are included.

const user = { id: 1, name: "Ada", token: "secret" };
const publicJson = JSON.stringify(user, ["id", "name"]);

console.log(publicJson);

Result:

{"id":1,"name":"Ada"}

Filter unknown fields

If you need a strict schema, discard unknown keys so extra fields do not leak into your domain model.

type PublicUser = { id: number; name: string };

function toPublicUser(value: Record<string, unknown>): PublicUser {
return { id: Number(value.id), name: String(value.name) };
}

const raw = JSON.parse('{"id":1,"name":"Ada","role":"admin"}') as Record<string, unknown>;
console.log(toPublicUser(raw));

Result:

{ id: 1, name: "Ada" }

Stable JSON for comparisons

JSON output is not stable if key order varies. If you compare JSON strings (for caching or hashing), normalize key order.

function stableStringify(value: Record<string, unknown>): string {
const sortedKeys = Object.keys(value).sort();
const normalized: Record<string, unknown> = {};
for (const key of sortedKeys) normalized[key] = value[key];
return JSON.stringify(normalized);
}

const left = stableStringify({ b: 2, a: 1 });
const right = stableStringify({ a: 1, b: 2 });

console.log(left === right);

Result:

true

Handling optional values

Prefer defaults when fields are missing. This avoids undefined checks everywhere.

type Settings = { theme?: string; pageSize?: number };

function normalizeSettings(raw: Settings): Required<Settings> {
return {
theme: raw.theme ?? "dark",
pageSize: raw.pageSize ?? 20,
};
}

const parsedSettings = JSON.parse('{"theme":"light"}') as Settings;
console.log(normalizeSettings(parsedSettings));

Result:

{ theme: "light", pageSize: 20 }

JSON limitations to remember

JSON does not support undefined, functions, or BigInt. Dates are serialized as strings. Plan for these conversions.

const payload = { id: 1, createdAt: new Date("2026-02-02T14:30:00Z") };
const text = JSON.stringify(payload);

console.log(text);

Result:

{"id":1,"createdAt":"2026-02-02T14:30:00.000Z"}

Redacting before logging

When you log parsed JSON, mask secrets or tokens to reduce risk.

function redact(value: Record<string, unknown>): Record<string, unknown> {
const copy = { ...value };
if (typeof copy.token === "string") copy.token = "***";
return copy;
}

const payload = { id: 1, token: "secret-token" };
console.log(redact(payload));

Result:

{ id: 1, token: "***" }

Avoid parsing JSON repeatedly

If you read the same JSON value often, parse once and reuse.

const jsonBlob = '{"items":[1,2,3,4]}';
const data = JSON.parse(jsonBlob) as { items: number[] };
const total = data.items.reduce((acc, v) => acc + v, 0);

console.log(total);

Result:

10

Common pitfalls

  • Trusting unvalidated JSON: parsing is not validation.
  • Ignoring parse errors: always handle invalid input.
  • Comparing raw JSON strings: key order can differ.
  • Assuming dates stay dates: they become strings.
  • Serializing sensitive data: avoid exposing secrets.

Best practices

  • Use safeParse helpers with explicit error handling.
  • Validate types with small type guards.
  • Keep JSON payloads small for frequent parsing.
  • Normalize optional fields to consistent shapes.
  • Document serialization rules in your API contract.

FAQ: JSON parsing in JavaScript

How do I parse JSON safely?

Use try/catch or a helper that returns a result object.

const parsed = safeParse<{ ok: boolean }>('{"ok": true}');
console.log(parsed.ok);

Result:

true

How do I parse JSON into a TypeScript type?

Parse to unknown, then validate with a type guard.

const raw = JSON.parse('{"id": 1, "name": "Ada"}') as unknown;
console.log(isUser(raw));

Result:

true

How do I stringify JSON with formatting?

Use the space parameter of JSON.stringify.

const formatted = JSON.stringify({ id: 1 }, null, 2);
console.log(formatted);

Result:

{
"id": 1
}

Summary

Safe JSON handling is about more than parsing. Validate shapes, handle errors, normalize defaults, and control serialization. Those habits make your data layer resilient and your code easier to maintain.