Read a JSON file in Node.js

Use asynchronous file reads and JSON.parse, understand require caching, and handle malformed JSON, BOMs, and newline-delimited records.

Best practices
Illustration accompanying a Node.js development guide

For application data that may change, read the file with node:fs/promises and parse its contents with JSON.parse(). For fixed configuration in CommonJS, require() is shorter but caches the parsed value.

Read and parse explicitly

// read-json.mjs
import { readFile } from 'node:fs/promises';

async function readJson(path) {
  const text = await readFile(path, 'utf8');
  return JSON.parse(text);
}

try {
  const config = await readJson('config.json');
  if (!config || typeof config !== 'object' || Array.isArray(config)) {
    throw new TypeError('Expected a configuration object');
  }
  console.log(config);
} catch (error) {
  console.error('Cannot load configuration:', error);
  process.exitCode = 1;
}

readFile reads asynchronously; JSON.parse still runs synchronously on the calling thread. A relative file path here is relative to the process's current working directory. To address a file beside this ES module, use new URL('./config.json', import.meta.url) instead.

Valid JSON can be an object, array, string, number, Boolean, or null. Parsing therefore does not replace schema validation. Validate the fields and types the application actually requires.

Callback-style reads

For code that uses callbacks, handle the read error first and catch parsing errors inside the callback:

const fs = require('node:fs');
fs.readFile('config.json', 'utf8', (error, text) => {
  if (error) {
    console.error('Read failed:', error);
    process.exitCode = 1;
    return;
  }
  try {
    const config = JSON.parse(text);
    console.log(config);
  } catch (error) {
    console.error('Invalid JSON:', error);
    process.exitCode = 1;
  }
});

A try block around the initial readFile call does not catch an exception thrown later in its asynchronous callback. Apply the same schema checks as in the promise-based version.

CommonJS configuration loading

const config = require('./config.json');
console.log(config);

This relative path resolves from the requiring module. Repeated loads normally return the cached parsed object, including mutations made by other consumers. Changing the file on disk does not automatically refresh it.

Deleting require.cache[require.resolve('./config.json')] allows a later require to load it again, but does not update references already held elsewhere. Explicit file reads are clearer for configuration that must be refreshed.

ES module JSON imports

Supported Node releases accept import attributes:

import config from './config.json' with { type: 'json' };

The attribute is required for JSON module imports, which provide a default export. Syntax and stability vary across older Node versions, so check the deployed minor release rather than assuming every Node 18 or 20 installation accepts the same form. Imported JSON is also a module-loading mechanism, not a live file watcher.

Diagnose parsing failures

JSON requires double-quoted property names and strings. Comments and trailing commas are not valid JSON. Handle read errors separately when useful: a missing file, denied access, and malformed content need different remedies.

A leading UTF-8 byte-order mark can make direct JSON.parse fail. If your input policy permits it, remove only that initial character with text.replace(/^\uFEFF/, '') before parsing. Do not silently treat malformed content as an empty configuration.

Large files and JSON Lines

Reading a large JSON document holds both its text and parsed representation in memory. A streaming parser such as stream-json can process suitable document structures incrementally. JSON Lines is a different format with one JSON value per line:

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const lines = createInterface({
  input: createReadStream('events.jsonl'), crlfDelay: Infinity
});
for await (const line of lines) {
  if (!line.trim()) continue;
  const record = JSON.parse(line);
  console.log(record);
}

Wrap that operation in application-level error handling. A single huge line can still use substantial memory, and ordinary pretty-printed JSON cannot be parsed one line at a time.

See Node file reads, CommonJS caching, and JSON modules.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting