Node.js環境において、JSONファイルを読み込む操作は、設定ファイルの取得やデータの永続化など、開発のあらゆる場面で頻繁に発生する基本的なタスクです。
長年にわたり、Node.jsではCommonJS形式の「require」を使用したシンプルな読み込みが一般的でしたが、近年のESモジュール(ESM)の普及やセキュリティ意識の高まりにより、その手法は多様化しています。
2026年現在のモダンな開発環境では、プロジェクトの構成や実行速度、メモリ効率に応じて、最適な読み込み手法を選択することが求められます。
本記事では、伝統的なfsモジュールによる処理から、最新のESM仕様に基づいたインポート属性(Import Attributes)まで、現場で役立つ具体的な実装方法を詳しく解説します。
1. Node.jsにおけるJSON読み込みの基本手法
Node.jsでJSONファイルを読み込む方法は、大きく分けて「同期的な読み込み」と「非同期的な読み込み」の2種類に分類されます。
同期的な読み込みはコードがシンプルになりますが、ファイル読み込み中にメインスレッドをブロックするため、サーバーアプリケーションでは注意が必要です。
一方で、非同期的な読み込みはNode.jsのノンブロッキングI/Oの特性を活かせるため、高パフォーマンスなアプリケーションに向いています。
また、モジュールシステムの違い(CommonJSかESMか)によって利用できる構文が異なる点も重要なポイントです。
2. CommonJSにおける伝統的なrequireによる読み込み
CommonJS(CJS)形式を採用している従来のプロジェクトでは、require関数を使用して簡単にJSONファイルを読み込むことが可能です。
この手法の最大のメリットは、拡張子を指定するだけでNode.jsが自動的にファイルをパースし、JavaScriptオブジェクトとして返してくれる点にあります。
// config.jsonの内容
// { "appName": "NodeApp", "version": "1.0.0" }
const config = require('./config.json');
console.log(config.appName);
NodeApp
requireを使用する際の注意点
非常に便利なrequireですが、いくつかの重要な仕様を理解しておく必要があります。
まず、requireで読み込まれたJSONは内部的にキャッシュされるという性質があります。
同じプロセス内でファイルを書き換えたとしても、再度requireを呼び出すと古いデータが返されるため、動的に更新されるデータの読み込みには適していません。
また、読み込みが「同期的」に行われるため、巨大なJSONファイルを読み込む際にはアプリケーション全体のパフォーマンスが一時的に低下する恐れがあります。
3. fsモジュールを使用した柔軟な読み込み方法
より制御の効いた、あるいは最新の環境でも汎用的に使える方法が、Node.js標準のfs(File System)モジュールを利用する手法です。
この方法では、ファイルをバイナリまたは文字列として読み込み、手動でJSON.parse()を適用します。
fs.readFileSyncによる同期読み込み
スクリプトの初期化段階など、実行順序を保証したい場合には同期版のメソッドが利用されます。
const fs = require('fs');
const path = require('path');
// ファイルパスを絶対パスで指定
const filePath = path.join(__dirname, 'data.json');
const rawData = fs.readFileSync(filePath, 'utf8');
const jsonData = JSON.parse(rawData);
console.log(jsonData);
fs.promisesによる非同期読み込み(推奨)
現代のNode.js開発において、最も推奨されるのが「fs/promises」を利用した非同期処理です。
async/await構文と組み合わせることで、可読性を維持しつつ非同期処理を実現できます。
const fs = require('fs').promises;
const path = require('path');
async function loadConfig() {
try {
const filePath = path.join(__dirname, 'config.json');
const data = await fs.readFile(filePath, 'utf8');
return JSON.parse(data);
} catch (error) {
console.error('ファイルの読み込みに失敗しました:', error);
}
}
loadConfig().then(config => console.log(config));
4. モダンなESモジュール(ESM)でのJSON読み込み
近年のNode.jsでは、拡張子が.mjsのファイルや、package.jsonで"type": "module"が指定されたプロジェクトが主流となっています。
ESM環境ではrequireが使用できないため、従来とは異なるアプローチが必要になります。
Import Attributes(旧Import Assertions)の利用
2026年現在、Node.jsの最新安定版では、import文で直接JSONを読み込む「Import Attributes」がサポートされています。
この機能により、静的な解析が可能な安全な方法でJSONをインポートできるようになります。
// ESモジュール内での記述
import config from './config.json' with { type: 'json' };
console.log(config.version);
以前はassert { type: 'json' }という構文でしたが、現在はwith { type: 'json' }が標準的な仕様となっています。
この方法で読み込まれたJSONは、requireと同様に読み込み時にパースされ、デフォルトエクスポートとして扱われます。
ESM環境での動的な読み込み
実行時にパスが決まるファイルをESM環境で読み込む場合は、fs.promisesとimport.meta.urlを組み合わせます。
ESMでは__dirnameが使用できないため、URL形式のパスをファイルパスに変換する手間が必要です。
import { readFile } from 'fs/promises';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const loadDynamicJSON = async (fileName) => {
const path = join(__dirname, fileName);
const content = await readFile(path, 'utf8');
return JSON.parse(content);
};
const data = await loadDynamicJSON('data.json');
console.log(data);
5. 実行環境と用途に応じた手法の比較
適切な手法を選択するために、それぞれの特徴を表にまとめました。
| 手法 | モジュール形式 | 同期/非同期 | 主な特徴・メリット |
|---|---|---|---|
require() | CommonJS | 同期 | 簡潔だがキャッシュされる。動的更新に弱い。 |
fs.readFileSync | 共通 | 同期 | シンプルだが処理をブロックする。初期化時に適向。 |
fs.promises.readFile | 共通 | 非同期 | 最も安全で高速。async/awaitが利用可能。 |
import ... with | ESM | 同期(静的) | 最新仕様。標準的な記述でセキュリティも高い。 |
6. 実践的なエラーハンドリングとバリデーション
JSONの読み込みには、常に「ファイルが存在しない」「JSONの形式が壊れている」といったリスクが伴います。
堅牢なアプリケーションを作成するためには、適切なエラーハンドリングが欠かせません。
JSON.parseの例外処理
JSON.parse()は、不正な形式の文字列が渡されると例外をスローします。
必ずtry...catchブロックで囲み、異常終了を防ぐようにしましょう。
function safeParse(text) {
try {
return JSON.parse(text);
} catch (e) {
console.error('JSONの解析に失敗しました。フォーマットを確認してください。');
return null;
}
}
ファイルの存在確認
ファイルを読み込む前に、fs.accessなどを使用して存在を確認することも有効ですが、読み込み処理の中でエラーをキャッチする方が効率的な場合が多いです。
Node.jsでは、存在しないファイルを読み込もうとすると、エラーコード'ENOENT'が発生します。
7. 大規模なJSONファイルを扱う場合の最適化
ファイルサイズが数MBから数百MBに及ぶ巨大なJSONを扱う場合、fs.readFileやJSON.parseを使用すると、一度に全てのデータをメモリに読み込むためメモリ不足(OOM)に陥る可能性があります。
大規模データを扱う場合は「ストリーム」の利用を検討してください。
Node.jsのfs.createReadStreamと、stream-jsonのようなライブラリを組み合わせることで、データを分割して少しずつ処理することが可能です。
これにより、メモリ消費量を一定に抑えつつ、効率的にデータを加工できます。
8. まとめ
Node.jsにおけるJSONファイルの読み込みは、シンプルなようで奥が深いトピックです。
2026年現在の開発においては、CommonJSならrequire、ESMならimport ... with属性を使用するのが最も手軽な方法です。
しかし、柔軟性やパフォーマンスを重視するならば、fs/promisesを用いた非同期読み込みが最良の選択肢となります。
また、ファイルのパス解決にはpathモジュールを適切に使用し、予期せぬエラーを防ぐために必ずエラーハンドリングを実装してください。
プロジェクトの要件や将来の拡張性を考慮し、今回紹介した手法の中から最適なものを選んでみてください。
