In Node.js, JSON.parse() and JSON.stringify() work exactly like they do in the browser, since they’re part of the V8 engine itself, not something specific to the Node environment. The real difference is in how JSON gets in and out of your program: reading from files, receiving it from HTTP requests, or sending it to a database. This post covers those Node-specific scenarios.
Reading a JSON file
The most common and direct way, when the file is small, is using require() directly.
// javascript
const data = require('./config.json');
console.log(data.name);
This works because Node natively recognizes .json files in require(). But notice: the result gets cached, so if the file changes after the program has already started, require() won’t reflect that change without restarting the process.
If you need to read the updated content every time (for example, a file that gets modified while the program is running), use the fs module.
// javascript
const fs = require('fs');
const text = fs.readFileSync('./config.json', 'utf-8');
const data = JSON.parse(text);
Async version (recommended in production code)
// javascript
const fs = require('fs').promises;
async function readConfig() {
const text = await fs.readFile('./config.json', 'utf-8');
return JSON.parse(text);
}
The synchronous version (readFileSync) blocks the event loop while reading the file, which is fine for simple scripts or app startup, but should be avoided in code that runs while handling requests.
Writing JSON to a file
// javascript
const fs = require('fs').promises;
async function saveConfig(data) {
const text = JSON.stringify(data, null, 2);
await fs.writeFile('./config.json', text, 'utf-8');
}
The null, 2 in JSON.stringify() formats the file with 2-space indentation, keeping it readable if someone opens it manually later.
Handling JSON in HTTP requests (Express)
When building an API, the express.json() middleware automatically parses the request body.
// javascript
const express = require('express');
const app = express();
app.use(express.json());
app.post('/data', (req, res) => {
console.log(req.body); // already a parsed JavaScript object
res.json({ status: 'ok' }); // res.json() already stringifies and sets the right header
});
Notice that res.json() already handles calling JSON.stringify() internally and sets the Content-Type: application/json header, so you usually don’t need to call JSON.stringify() manually in an Express route.
Consuming an external API (fetch/axios)
// javascript
const response = await fetch('https://api.example.com/data');
const data = await response.json(); // already parsed
fetch‘s .json() method already runs JSON.parse() internally. It’s common to see code that unnecessarily calls JSON.parse(await response.text()), when .json() already handles that in one line.
Environment variables and JSON config files
A common pattern in Node projects is having part of the configuration in JSON and allowing it to be overridden with environment variables.
// javascript
const defaultConfig = require('./config.json');
const config = {
...defaultConfig,
port: process.env.PORT || defaultConfig.port,
};
Quick checklist
- Using
require()only for static JSON files that don’t change while the program runs? - Preferring the async version (
fs.promises) in code that runs during requests? - Using
express.json()(or equivalent) instead of manually parsing the request body? - Using fetch’s
.json()instead ofJSON.parse(await response.text())?
To quickly validate a JSON before saving it to a config file, paste it into JSON Validator.