How to Parse JSON in JavaScript: JSON.parse(), fetch() and Error Handling
Parsing JSON in JavaScript is a daily task for every web developer. This guide covers everything from the basics of JSON.parse() to fetching remote JSON, handling errors correctly, and typing the result in TypeScript — with copy-paste examples for each pattern.
The Basics: JSON.parse()
JSON.parse() converts a JSON string into a JavaScript value. It accepts any valid JSON: object, array, string, number, boolean, or null.
const json = '{"name": "Alice", "age": 30, "active": true}';
const user = JSON.parse(json);
console.log(user.name); // "Alice"
console.log(user.age); // 30
console.log(user.active); // trueAlways Use try/catch
JSON.parse() throws a SyntaxError if the input isn't valid JSON. Never call it without error handling on untrusted input:
function safeJsonParse(text) {
try {
return { data: JSON.parse(text), error: null };
} catch (e) {
return { data: null, error: e.message };
}
}
const { data, error } = safeJsonParse('not valid json');
if (error) {
console.error('Parse failed:', error);
} else {
console.log(data);
}Fetching JSON from an API
The Fetch API returns a Response object. Call .json() on it — which internally calls JSON.parse() — to get the parsed value:
async function getUser(id) {
const response = await fetch(`https://api.example.com/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const user = await response.json(); // JSON.parse() internally
return user;
}
// Usage
try {
const user = await getUser(42);
console.log(user);
} catch (e) {
console.error('Failed to fetch user:', e.message);
}Check response.ok before .json()
Fetch only rejects on network failures, not HTTP errors (404, 500). Always check response.ok (true when status is 200-299) before calling response.json(), otherwise you might parse an error HTML page as JSON and get a confusing SyntaxError.
The Reviver Function
Pass a reviver as the second argument to transform values during parsing — the most common use case is parsing ISO date strings into real Date objects:
const json = '{"name":"Alice","createdAt":"2026-10-14T08:00:00.000Z"}';
const user = JSON.parse(json, (key, value) => {
if (key === 'createdAt') return new Date(value);
return value;
});
console.log(user.createdAt instanceof Date); // true
console.log(user.createdAt.getFullYear()); // 2026JSON.stringify() — The Reverse
Serialize a JavaScript value back to a JSON string with JSON.stringify(). The optional third argument sets indentation for pretty-printing:
const user = { name: 'Alice', age: 30, createdAt: new Date() };
// Compact (for APIs)
const compact = JSON.stringify(user);
// Pretty (for logging / files)
const pretty = JSON.stringify(user, null, 2);
console.log(pretty);
// {
// "name": "Alice",
// "age": 30,
// "createdAt": "2026-10-14T08:00:00.000Z"
// }TypeScript: Typing Parsed JSON
JSON.parse() returns any in TypeScript. Cast or validate to get type safety:
// Option 1: Type assertion (no runtime check)
interface User {
id: number;
name: string;
email: string;
}
const user = JSON.parse(jsonString) as User;
// Option 2: Runtime validation with Zod (recommended)
import { z } from 'zod';
const UserSchema = z.object({
id: z.number(),
name: z.string(),
email: z.string().email(),
});
type User = z.infer<typeof UserSchema>;
function parseUser(raw: string): User {
const data = JSON.parse(raw);
return UserSchema.parse(data); // throws ZodError if invalid
}
// Option 3: Safe parse (no throw)
const result = UserSchema.safeParse(JSON.parse(raw));
if (result.success) {
console.log(result.data.name);
} else {
console.error(result.error.issues);
}Parsing JSON from Node.js — Files and Streams
import { readFileSync, createReadStream } from 'fs';
// Small file — synchronous (blocks event loop)
const config = JSON.parse(readFileSync('./config.json', 'utf8'));
// Or with require (also synchronous, cached)
const pkg = require('./package.json');
// Large file — streaming with a library
import JSONStream from 'JSONStream';
import { pipeline } from 'stream/promises';
const stream = createReadStream('./large.json');
stream
.pipe(JSONStream.parse('rows.*'))
.on('data', (row) => console.log(row));Common Pitfalls
| Pitfall | Fix |
|---|---|
| Trailing comma in object/array | Use a linter; JSON forbids trailing commas |
| Single-quoted keys or values | JSON requires double quotes everywhere |
| NaN or Infinity in numbers | Replace with null or use a replacer |
Forgetting await on response.json() | Always await — it returns a Promise |
Not checking response.ok | Check before calling .json() |
| Parsing JSON twice (double-encode) | Parse once; log intermediate values to check |
Validate before parsing
If you're unsure whether your JSON is valid, paste it into the JSON Validator first. It pinpoints the exact line and character position of every syntax error, saving you debugging time.