JSON in Node.js
Node.js has first-class JSON support built in through the V8 engine and the core fs module. This guide covers every common pattern: reading files, writing files, building REST API endpoints, streaming large JSON datasets, and validating with JSON Schema. Paste any Node.js API response into the JSON Formatter to inspect and debug it quickly.
Reading a JSON File
The simplest way to read a JSON file is with require() — Node.js parses it automatically:
const config = require("./config.json");
console.log(config.port); // read directly, no JSON.parse neededrequire() caches the result. For files that change at runtime, use fs instead:
const fs = require("fs");
// Synchronous (blocks the event loop — only use at startup)
const raw = fs.readFileSync("data.json", "utf8");
const data = JSON.parse(raw);
// Asynchronous (preferred)
fs.readFile("data.json", "utf8", (err, raw) => {
if (err) throw err;
const data = JSON.parse(raw);
console.log(data);
});
// Promise-based (Node 10+, recommended)
const { readFile } = require("fs/promises");
async function loadConfig() {
const raw = await readFile("config.json", "utf8");
return JSON.parse(raw);
}Writing a JSON File
const { writeFile } = require("fs/promises");
async function saveUser(user) {
await writeFile("user.json", JSON.stringify(user, null, 2), "utf8");
}
// Minified (no indent):
await writeFile("data.json", JSON.stringify(data));
// Pretty-printed (2-space indent):
await writeFile("data.json", JSON.stringify(data, null, 2));Safe JSON Parse (Never Crash)
Always wrap JSON.parse in a try/catch when reading external or user-supplied data:
function safeParseJSON(str, fallback = null) {
try {
return JSON.parse(str);
} catch {
return fallback;
}
}
const data = safeParseJSON(rawString, {});Building a JSON REST API with http Module
const http = require("http");
const users = [
{ id: 1, name: "Ravi Mehta", email: "ravi@example.com" },
{ id: 2, name: "Priya Sharma", email: "priya@example.com" },
];
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
if (req.method === "GET" && req.url === "/users") {
res.writeHead(200);
res.end(JSON.stringify({ success: true, data: users }));
return;
}
if (req.method === "POST" && req.url === "/users") {
let body = "";
req.on("data", chunk => (body += chunk));
req.on("end", () => {
try {
const newUser = JSON.parse(body);
newUser.id = users.length + 1;
users.push(newUser);
res.writeHead(201);
res.end(JSON.stringify({ success: true, data: newUser }));
} catch {
res.writeHead(400);
res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
}
});
return;
}
res.writeHead(404);
res.end(JSON.stringify({ success: false, error: "Not found" }));
});
server.listen(3000, () => console.log("Server running on :3000"));Building a JSON API with Express
const express = require("express");
const app = express();
app.use(express.json()); // parse incoming JSON bodies automatically
app.get("/users", (req, res) => {
res.json({ success: true, data: users });
});
app.post("/users", (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ success: false, error: "name and email are required" });
}
const user = { id: users.length + 1, name, email };
users.push(user);
res.status(201).json({ success: true, data: user });
});
app.listen(3000);Streaming Large JSON Files
For JSON files larger than a few hundred MB, loading the entire file into memory with JSON.parse will crash Node.js with an out-of-memory error. Use the stream-json package instead:
npm install stream-jsonconst { createReadStream } = require("fs");
const { parser } = require("stream-json");
const { streamArray } = require("stream-json/streamers/StreamArray");
const pipeline = createReadStream("large-users.json")
.pipe(parser())
.pipe(streamArray());
pipeline.on("data", ({ value }) => {
// process each user object one at a time — never all in memory
console.log(value.name);
});
pipeline.on("end", () => console.log("Done"));This approach handles multi-gigabyte JSON arrays with constant memory usage.
Writing JSON Line by Line (NDJSON)
For very large datasets, write one JSON object per line (NDJSON format) to avoid building one giant array in memory:
const { createWriteStream } = require("fs");
const out = createWriteStream("output.ndjson");
for (const record of records) {
out.write(JSON.stringify(record) + "\n");
}
out.end();JSON Schema Validation in Node.js
Validate incoming API data against a schema using Ajv:
npm install ajv ajv-formatsconst Ajv = require("ajv");
const addFormats = require("ajv-formats");
const ajv = new Ajv({ allErrors: true });
addFormats(ajv);
const userSchema = {
type: "object",
properties: {
name: { type: "string", minLength: 1, maxLength: 100 },
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0, maximum: 150 },
},
required: ["name", "email"],
additionalProperties: false,
};
const validate = ajv.compile(userSchema);
app.post("/users", (req, res) => {
if (!validate(req.body)) {
return res.status(400).json({ errors: validate.errors });
}
// safe to use req.body here
});Serializing Special Types
Dates — JSON has no date type; serialize as ISO 8601 strings:
const obj = {
name: "Ravi",
createdAt: new Date().toISOString(), // "2025-06-01T12:00:00.000Z"
};BigInt — JSON.stringify throws for BigInt; convert to string:
const safeStringify = (obj) =>
JSON.stringify(obj, (key, val) =>
typeof val === "bigint" ? val.toString() : val
);Circular references — use the flatted package or a replacer to remove circular refs:
const { stringify } = require("flatted");
console.log(stringify(circularObj)); // no crashPerformance Tips
- Use
JSON.stringifyandJSON.parsedirectly — they call into native V8 code and are extremely fast - For performance-critical paths, consider
fast-json-stringify(up to 2× faster serialization for known schemas) - Avoid
JSON.parse(JSON.stringify(obj))for deep cloning — usestructuredClone(obj)in Node 17+ - Stream large files instead of loading them into memory