jsonnodejsjavascriptapitutorial

JSON in Node.js: File I/O, HTTP Servers, and Streams

·10 min read

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:

javascript
const config = require("./config.json");
console.log(config.port); // read directly, no JSON.parse needed

require() caches the result. For files that change at runtime, use fs instead:

javascript
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

javascript
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:

javascript
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

javascript
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

javascript
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:

bash
npm install stream-json
javascript
const { 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:

javascript
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:

bash
npm install ajv ajv-formats
javascript
const 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:

javascript
const obj = {
  name: "Ravi",
  createdAt: new Date().toISOString(), // "2025-06-01T12:00:00.000Z"
};

BigInt — JSON.stringify throws for BigInt; convert to string:

javascript
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:

javascript
const { stringify } = require("flatted");
console.log(stringify(circularObj)); // no crash

Performance Tips

  • Use JSON.stringify and JSON.parse directly — 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 — use structuredClone(obj) in Node 17+
  • Stream large files instead of loading them into memory

Try JSON Formatter

Format and validate JSON responses as you build your Node.js APIs.