16 minLesson 16 of 35
Arrays & Objects
JSON: Parse, Stringify & Working with APIs
JSON: Parse, Stringify & Working with APIs
JSON (JavaScript Object Notation) is the universal format for data exchange on the web. Every API you call sends and receives JSON. Understanding it deeply — including its quirks and limitations — is non-negotiable for web development.
JSON Syntax
JSON looks like JavaScript objects but has stricter rules:
{
"name": "Alice",
"age": 30,
"active": true,
"scores": [95, 87, 92],
"address": {
"city": "New York",
"zip": "10001"
},
"notes": null
}
JSON rules:
- Keys must be double-quoted strings
- Values: string, number, boolean, array, object, or null
- No trailing commas
- No comments
- No undefined, functions, Dates, or symbols
JSON.stringify — Object to String
const user = { name: "Alice", age: 30, active: true };
JSON.stringify(user);
// '{"name":"Alice","age":30,"active":true}'
// Pretty-print (3rd argument is indent spaces)
JSON.stringify(user, null, 2);
// {
// "name": "Alice",
// "age": 30,
// "active": true
// }
// What gets dropped:
const messy = {
name: "Alice",
fn: () => "hello", // ← dropped (functions)
undef: undefined, // ← dropped (undefined)
sym: Symbol("id"), // ← dropped (symbols)
date: new Date(), // ← converted to ISO string
num: Infinity, // ← becomes null
nan: NaN // ← becomes null
};
JSON.stringify(messy, null, 2);
// { "name": "Alice", "date": "2026-05-26T...", "num": null, "nan": null }
Replacer Function
// Only include specific keys
JSON.stringify(user, ["name", "active"]);
// '{"name":"Alice","active":true}'
// Transform values during serialization
const data = { name: "Alice", password: "secret123", age: 30 };
JSON.stringify(data, (key, value) => {
if (key === "password") return undefined; // omit
return value;
});
// '{"name":"Alice","age":30}'
toJSON Method
Objects can define how they serialize themselves:
class User {
constructor(name, password) {
this.name = name;
this.password = password;
}
toJSON() {
// Only expose safe fields
return { name: this.name };
}
}
JSON.stringify(new User("Alice", "secret"));
// '{"name":"Alice"}'
JSON.parse — String to Object
const json = '{"name":"Alice","age":30,"scores":[95,87]}';
const user = JSON.parse(json);
user.name; // "Alice"
user.scores[0]; // 95
// With reviver function — transform values during parsing
const dateJson = '{"name":"Alice","createdAt":"2026-01-15T10:30:00.000Z"}';
const parsed = JSON.parse(dateJson, (key, value) => {
if (key === "createdAt") return new Date(value); // convert to Date object
return value;
});
parsed.createdAt instanceof Date; // true
parsed.createdAt.getFullYear(); // 2026
Error Handling
JSON.parse throws on invalid JSON. Always wrap it:
function safeParseJSON(str, fallback = null) {
try {
return JSON.parse(str);
} catch {
return fallback;
}
}
safeParseJSON('{"valid": true}'); // { valid: true }
safeParseJSON('not json at all'); // null
safeParseJSON('undefined', {}); // {}
// In practice — API responses
async function fetchUser(id) {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) {
const errorText = await res.text();
const error = safeParseJSON(errorText, { message: errorText });
throw new Error(error.message ?? "Request failed");
}
return res.json(); // fetch's .json() already parses + throws on bad JSON
}
Working with APIs
// GET request — receive JSON
const response = await fetch("https://api.example.com/users");
const users = await response.json(); // parses JSON automatically
// POST request — send JSON
const newUser = { name: "Alice", email: "alice@example.com" };
const response = await fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}`
},
body: JSON.stringify(newUser) // serialize before sending
});
const created = await response.json();
console.log(created.id); // the ID assigned by the server
Local Storage with JSON
localStorage only stores strings, so JSON is used to serialize/deserialize:
// Save
const cart = [{ id: 1, qty: 2 }, { id: 3, qty: 1 }];
localStorage.setItem("cart", JSON.stringify(cart));
// Load
const saved = localStorage.getItem("cart");
const cart = saved ? JSON.parse(saved) : [];
// Utility wrapper
const storage = {
get(key, fallback = null) {
return safeParseJSON(localStorage.getItem(key), fallback);
},
set(key, value) {
localStorage.setItem(key, JSON.stringify(value));
},
remove(key) {
localStorage.removeItem(key);
}
};
storage.set("preferences", { theme: "dark", language: "en" });
storage.get("preferences"); // { theme: "dark", language: "en" }
Deep Cloning with JSON
A quick way to deep clone objects — but with limitations:
// Works for: strings, numbers, booleans, arrays, plain objects
const original = { a: 1, b: { c: 2 }, d: [3, 4] };
const clone = JSON.parse(JSON.stringify(original));
clone.b.c = 99;
original.b.c; // 2 — unaffected
// Does NOT work for: Dates, functions, undefined, circular references
const withDate = { date: new Date(), fn: () => {} };
JSON.parse(JSON.stringify(withDate));
// { date: "2026-05-26T..." } — Date becomes string, fn is dropped!
// Better alternatives:
// structuredClone (built-in, handles more types)
const clone2 = structuredClone(original);
// lodash deepClone
import cloneDeep from "lodash/cloneDeep";
const clone3 = cloneDeep(withDate); // preserves Dates, handles circular refs
Next lesson: DOM Manipulation — selecting and modifying HTML elements from JavaScript.
📱
Get Notes Free →Get this course's notes on Telegram!
Free cheat sheets, summaries & practice exercises