Working with JSON
JSON (JavaScript Object Notation) is the most common data format in modern web development. Node.js provides built-in support for parsing and creating JSON, making it essential for APIs, configuration files, and data storage.
What is JSON?
JSON is a lightweight text format for storing and transmitting data. It's based on JavaScript object syntax but is language-independent:
{
"name": "Alice",
"age": 30,
"email": "alice@example.com",
"isActive": true,
"roles": ["admin", "user"],
"address": {
"city": "New York",
"country": "USA"
}
}
JSON supports these data types:
- String:
"hello" - Number:
42,3.14,-10 - Boolean:
true,false - Null:
null - Array:
[1, 2, 3] - Object:
{"key": "value"}
JSON.parse(): String to Object
Convert a JSON string into a JavaScript object:
const jsonString = '{"name": "Alice", "age": 30}';
const user = JSON.parse(jsonString);
console.log(user.name); // "Alice"
console.log(user.age); // 30
JSON.stringify(): Object to String
Convert a JavaScript object into a JSON string:
const user = { name: 'Alice', age: 30 };
const jsonString = JSON.stringify(user);
console.log(jsonString); // '{"name":"Alice","age":30}'
Pretty Printing
Add indentation for readable output:
const user = { name: 'Alice', age: 30, roles: ['admin'] };
// Compact (default)
console.log(JSON.stringify(user));
// {"name":"Alice","age":30,"roles":["admin"]}
// Pretty with 2-space indent
console.log(JSON.stringify(user, null, 2));
// {
// "name": "Alice",
// "age": 30,
// "roles": [
// "admin"
// ]
// }
The Replacer Parameter
Control what gets included in the JSON output:
const user = {
name: 'Alice',
password: 'secret123', // Don't include this!
email: 'alice@example.com',
age: 30
};
// Array replacer - only include specified keys
const json = JSON.stringify(user, ['name', 'email'], 2);
// { "name": "Alice", "email": "alice@example.com" }
// Function replacer - transform values
const safe = JSON.stringify(user, (key, value) => {
if (key === 'password') return undefined; // Exclude
return value;
}, 2);
The Reviver Parameter
Transform values during parsing:
const json = '{"date": "2024-01-15", "count": "42"}';
// Reviver to convert types
const data = JSON.parse(json, (key, value) => {
if (key === 'date') return new Date(value);
if (key === 'count') return parseInt(value, 10);
return value;
});
console.log(data.date instanceof Date); // true
console.log(typeof data.count); // "number"
Reading JSON Files
Load and parse JSON files in Node.js:
const fs = require('fs');
// Synchronous
const data = JSON.parse(fs.readFileSync('config.json', 'utf8'));
// Asynchronous with callback
fs.readFile('config.json', 'utf8', (err, content) => {
if (err) throw err;
const data = JSON.parse(content);
console.log(data);
});
// Async/await
const fsPromises = require('fs/promises');
async function loadConfig() {
const content = await fsPromises.readFile('config.json', 'utf8');
return JSON.parse(content);
}
Using require() for JSON
Node.js can directly require JSON files:
// This works but has caveats
const config = require('./config.json');
// The file is cached - changes won't be reflected
// Cannot use dynamic paths
Writing JSON Files
Save JavaScript objects as JSON files:
const fs = require('fs');
const config = {
port: 3000,
database: 'mongodb://localhost/myapp',
debug: true
};
// Synchronous
fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
// Asynchronous
fs.writeFile('config.json', JSON.stringify(config, null, 2), (err) => {
if (err) throw err;
console.log('Config saved!');
});
Handling JSON Errors
JSON operations can fail. Always handle errors:
// Parsing errors
function safeParse(jsonString) {
try {
return { data: JSON.parse(jsonString), error: null };
} catch (err) {
return { data: null, error: err.message };
}
}
const result = safeParse('invalid json');
if (result.error) {
console.log('Parse error:', result.error);
}
Common JSON Errors
1. Invalid Syntax
// Common mistakes
JSON.parse("{'name': 'Alice'}"); // Single quotes - Error!
JSON.parse('{name: "Alice"}'); // Unquoted key - Error!
JSON.parse('{"name": undefined}'); // undefined not valid - Error!
// Correct
JSON.parse('{"name": "Alice"}');
2. Trailing Commas
// Error - trailing comma
JSON.parse('{"a": 1, "b": 2,}');
// Correct
JSON.parse('{"a": 1, "b": 2}');
3. Circular References
const obj = { name: 'test' };
obj.self = obj; // Circular reference
// This will throw an error!
JSON.stringify(obj); // TypeError: circular structure
JSON with Dates
JSON doesn't have a date type. Use strings and convert:
const event = {
name: 'Meeting',
date: new Date().toISOString()
};
const json = JSON.stringify(event);
// {"name":"Meeting","date":"2024-01-15T10:30:00.000Z"}
// When parsing, convert back
const parsed = JSON.parse(json);
parsed.date = new Date(parsed.date);
Cloning Objects with JSON
A quick way to deep clone objects:
const original = {
name: 'Alice',
address: { city: 'NYC' }
};
// Deep clone using JSON
const clone = JSON.parse(JSON.stringify(original));
// Modifying clone doesn't affect original
clone.address.city = 'LA';
console.log(original.address.city); // "NYC"
console.log(clone.address.city); // "LA"
Limitations:
- Loses functions, undefined, symbols
- Doesn't work with circular references
- Date objects become strings
Working with API Responses
JSON is the standard format for APIs:
// Simulated API response
const apiResponse = `{
"status": "success",
"data": {
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
],
"total": 2
}
}`;
const response = JSON.parse(apiResponse);
if (response.status === 'success') {
response.data.users.forEach(user => {
console.log(`User ${user.id}: ${user.name}`);
});
}
JSON Schema Validation
For complex applications, validate JSON against a schema:
function validateUser(user) {
const errors = [];
if (!user.name || typeof user.name !== 'string') {
errors.push('name is required and must be a string');
}
if (!user.email || !user.email.includes('@')) {
errors.push('valid email is required');
}
if (user.age !== undefined && typeof user.age !== 'number') {
errors.push('age must be a number');
}
return {
valid: errors.length === 0,
errors
};
}
Key Takeaways
- Use
JSON.parse()to convert JSON strings to objects - Use
JSON.stringify()to convert objects to JSON strings - Add
null, 2to stringify for pretty-printed output - Always handle parse errors with try/catch
- JSON only supports strings, numbers, booleans, null, arrays, and objects
- Dates become strings in JSON - convert them back after parsing
- Avoid circular references when stringifying
- Use replacer to filter sensitive data from output
Summary
JSON is essential for Node.js development. You've learned how to parse and stringify JSON, handle errors gracefully, work with files, and process API responses. These skills are fundamental for building APIs, working with configuration files, and exchanging data between services.
Next, you'll learn about configuration patterns that build on these JSON skills to create robust, maintainable application configurations.

