Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to import JSON in Node.js

Loading JSON data is fundamental in Node.js applications, whether for configuration files, test fixtures, or static data sets. With 12 years of experience building Node.js applications since 2014 and as the creator of CoreUI, I’ve worked with JSON imports across countless production systems. The most straightforward approach depends on your module system: use require() for CommonJS or import with assertions for ES Modules. For dynamic loading or runtime flexibility, use fs.readFile() with JSON.parse() to load JSON files on demand.

Use require() for CommonJS modules to import JSON files directly into your Node.js application.

// data.json
{
  "name": "CoreUI",
  "version": "5.0.0",
  "features": ["responsive", "customizable", "accessible"]
}
// app.js (CommonJS)
const data = require('./data.json')

console.log(data.name)
console.log(data.version)
console.log(data.features)

This code loads the JSON file synchronously and parses it automatically. The require() function reads the file, parses the JSON content, and caches the result, so subsequent requires return the cached object without re-reading the file. This approach works seamlessly in CommonJS modules and is the most common pattern for loading configuration files.

Using ESM Import with Assertions

ES Modules require import assertions to load JSON files, which explicitly declare the module type.

// app.mjs (ES Module)
import data from './data.json' with { type: 'json' }

console.log(data.name)
console.log(data.version)
console.log(data.features)
// package.json
{
  "type": "module"
}

The with { type: 'json' } assertion tells Node.js to parse the file as JSON. This syntax is part of the import assertions proposal and replaces the older assert keyword. When "type": "module" is set in package.json, all .js files are treated as ES Modules, requiring this assertion syntax for JSON imports.

Dynamic JSON Loading with fs.readFile

For runtime flexibility or loading JSON based on user input, use the file system module with async/await.

import { readFile } from 'fs/promises'

const loadJSON = async (filePath) => {
  const data = await readFile(filePath, 'utf-8')
  return JSON.parse(data)
}

const config = await loadJSON('./config.json')
console.log(config)

This approach reads the file asynchronously and parses the JSON content manually. Unlike require() or import, this doesn’t cache the result, so each call reads from disk. Use this pattern when you need to reload configuration files without restarting the server or when the file path is determined at runtime.

Synchronous JSON Loading

When synchronous loading is required, use the sync version of readFile.

import { readFileSync } from 'fs'
import { resolve } from 'path'

const loadJSONSync = (filePath) => {
  const absolutePath = resolve(filePath)
  const data = readFileSync(absolutePath, 'utf-8')
  return JSON.parse(data)
}

const settings = loadJSONSync('./settings.json')
console.log(settings)

The resolve() function converts relative paths to absolute paths, ensuring the file is found regardless of the current working directory. Synchronous file operations block the event loop, so use them only during application startup or in scripts where async operations aren’t practical.

Handling JSON Parse Errors

JSON files can be malformed or corrupted, so always handle parsing errors gracefully.

import { readFile } from 'fs/promises'

const loadSafeJSON = async (filePath) => {
  try {
    const data = await readFile(filePath, 'utf-8')
    return JSON.parse(data)
  } catch (error) {
    if (error.code === 'ENOENT') {
      console.error(`File not found: ${filePath}`)
      return null
    }

    if (error instanceof SyntaxError) {
      console.error(`Invalid JSON in ${filePath}: ${error.message}`)
      return null
    }

    throw error
  }
}

const config = await loadSafeJSON('./config.json')
if (config) {
  console.log('Config loaded successfully')
}

This function catches file system errors (like missing files) and JSON parsing errors separately. The ENOENT error code indicates a missing file, while SyntaxError indicates invalid JSON syntax. Returning null for errors allows the application to continue with default values or alternative configuration sources.

Loading Multiple JSON Files

Applications often need to load multiple JSON files for different purposes.

import { readFile } from 'fs/promises'
import { resolve } from 'path'

const loadMultipleJSON = async (filePaths) => {
  const promises = filePaths.map(async (filePath) => {
    const absolutePath = resolve(filePath)
    const data = await readFile(absolutePath, 'utf-8')
    return JSON.parse(data)
  })

  return await Promise.all(promises)
}

const [config, users, settings] = await loadMultipleJSON([
  './config.json',
  './users.json',
  './settings.json'
])

console.log('Config:', config)
console.log('Users:', users)
console.log('Settings:', settings)

The Promise.all() method loads all files in parallel, improving performance compared to sequential loading. Destructuring the result array assigns each loaded JSON object to a named variable. If any file fails to load, the entire operation rejects, so consider using Promise.allSettled() if you need partial results.

Creating a JSON Cache

For frequently accessed JSON files, implement a simple cache to avoid repeated disk reads.

import { readFile } from 'fs/promises'
import { resolve } from 'path'

class JSONCache {
  constructor() {
    this.cache = new Map()
  }

  async load(filePath) {
    const absolutePath = resolve(filePath)

    if (this.cache.has(absolutePath)) {
      return this.cache.get(absolutePath)
    }

    const data = await readFile(absolutePath, 'utf-8')
    const parsed = JSON.parse(data)

    this.cache.set(absolutePath, parsed)
    return parsed
  }

  clear(filePath) {
    if (filePath) {
      this.cache.delete(resolve(filePath))
    } else {
      this.cache.clear()
    }
  }
}

const jsonCache = new JSONCache()

const config1 = await jsonCache.load('./config.json')
const config2 = await jsonCache.load('./config.json') // Returns cached version

jsonCache.clear('./config.json') // Clear specific file
jsonCache.clear() // Clear all cached files

This cache stores parsed JSON in a Map using absolute file paths as keys. The first load reads from disk, but subsequent loads return the cached object immediately. The clear() method allows invalidating specific files or the entire cache, useful when configuration changes during runtime.

Loading JSON from URLs

Node.js can fetch and parse JSON from remote URLs using the native fetch API.

const loadRemoteJSON = async (url) => {
  const response = await fetch(url)

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`)
  }

  return await response.json()
}

const data = await loadRemoteJSON('https://api.example.com/data.json')
console.log(data)

The fetch() API is available in Node.js 18+ without requiring external packages. The response.json() method automatically parses the response body as JSON. Always check response.ok before parsing to handle HTTP errors like 404 or 500 status codes gracefully.

Using JSON for Configuration Files

JSON files are ideal for storing application configuration that needs to be easily editable without code changes.

// config/database.json
{
  "host": "localhost",
  "port": 5432,
  "database": "myapp",
  "username": "admin",
  "poolSize": 10,
  "ssl": false
}
// config/loader.js
import { readFile } from 'fs/promises'
import { resolve } from 'path'

class ConfigLoader {
  constructor() {
    this.configs = new Map()
  }

  async load(environment = 'development') {
    const configPath = resolve(`./config/${environment}.json`)

    try {
      const data = await readFile(configPath, 'utf-8')
      const config = JSON.parse(data)

      this.configs.set(environment, config)
      return config
    } catch (error) {
      console.error(`Failed to load config for ${environment}:`, error.message)
      return null
    }
  }

  get(environment = 'development') {
    return this.configs.get(environment)
  }
}

const configLoader = new ConfigLoader()
export default configLoader

This configuration loader reads environment-specific JSON files and caches them in memory. The loader handles errors gracefully and allows retrieving configurations without repeated file reads. This pattern is commonly used in production applications to separate configuration from code.

Best Practice Note

This is the same approach we use in CoreUI build scripts and configuration systems to load JSON files reliably across different module systems. For working with different module formats, check out how to use ESM modules in Node.js and how to use CommonJS modules in Node.js. Always validate JSON schemas when loading external configuration to prevent runtime errors from malformed data. When building Node.js applications that integrate with CoreUI, use typed configuration objects with JSON schema validation to catch errors early and provide better developer experience.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
What is CoreUI and Why Should You Use It for Your Next Admin Dashboard?
What is CoreUI and Why Should You Use It for Your Next Admin Dashboard?

How to Center a Button in CSS
How to Center a Button in CSS

How to convert a string to boolean in JavaScript
How to convert a string to boolean in JavaScript

JavaScript printf equivalent
JavaScript printf equivalent

Answers by CoreUI Core Team