Node.jsにおいて、ファイル操作はアプリケーション開発の根幹を成す重要な要素の一つです。

特に設定ファイルの読み込みや、サーバー起動時の初期化プロセスにおいて、ファイルを同期的に読み込む必要がある場面は少なくありません。

一般的にNode.jsは非同期処理を得意としますが、特定のユースケースでは同期処理の方がコードを簡潔に保てるというメリットがあります。

本記事では、Node.jsのfsモジュールを使用してファイルを同期的に読み込む方法について、最新の標準的な手法を交えて解説します。

実行時のパフォーマンスへの影響や、非同期処理との使い分けの基準についても詳しく触れていきます。

Node.jsでファイルを同期的に読み込む基本メソッド

Node.jsでファイルを同期的に読み込むには、標準モジュールであるfs(File System)モジュールのfs.readFileSyncを使用します。

このメソッドは、ファイルの読み込みが完了するまでメインスレッドの処理を一時的に停止(ブロック)させるという特性を持っています。

fs.readFileSyncの基本的な使い方

まずは、最もシンプルなテキストファイルの読み込み例を見てみましょう。

同期的なメソッドを使用する場合、コールバック関数を渡す必要がなく、戻り値として直接ファイルの内容を受け取ることができます。

JavaScript
import { readFileSync } from 'node:fs';

// ファイルを同期的に読み込む
// 第1引数にパス、第2引数にエンコーディングを指定します
const data = readFileSync('./example.txt', 'utf8');

console.log('ファイルの内容を表示します:');
console.log(data);
実行結果
ファイルの内容を表示します:
これはテストファイルの内容です。

上記のコードでは、readFileSyncの第2引数に'utf8'を指定しています。

これにより、バイナリデータではなく文字列として内容を取得することが可能になります。

エンコーディングを指定しない場合の挙動

もし第2引数のエンコーディングを省略した場合、戻り値はBuffer(バッファ)オブジェクトとして返されます。

Bufferはバイナリデータを扱うためのオブジェクトであり、画像ファイルや実行ファイルなどを読み込む際に適しています。

JavaScript
import { readFileSync } from 'node:fs';

// エンコーディングを指定せずに読み込む
const bufferData = readFileSync('./example.txt');

console.log(bufferData);
実行結果
<Buffer e3 81 93 e3 82 8c e3 81 af ... 20 bytes more>

テキストとして内容を確認したい場合は、必ず'utf8'などの適切なエンコーディングを指定するようにしてください。

同期読み込みと非同期読み込みの使い分け

Node.jsの最大の特徴はシングルスレッドによるイベントループ構成であるため、同期処理の多用には注意が必要です。

どのような状況で同期読み込みを選択すべきか、その判断基準を明確にする必要があります。

同期処理(Sync)を選択すべきケース

同期処理は、プログラムの実行を一時停止させるため、後続の処理がそのファイルの内容に依存している場合に適しています。

具体的な利用シーンとしては、以下のようなケースが挙げられます。

  • アプリケーション起動時の設定ファイル(config.json)の読み込み
  • CLIツール(コマンドラインインターフェース)での逐次的なスクリプト実行
  • プログラムの初期化プロセスにおけるリソースの準備

これらのケースでは、ファイルが読み込まれない限り次のステップへ進む意味がないため、同期処理を用いることでコードの可読性を高めることができます。

非同期処理(Async)を選択すべきケース

一方で、Webサーバーのように同時に多くのリクエストを処理する環境では、同期処理は避けるべきです。

同期読み込みを行っている間、他のクライアントからのリクエストに対するレスポンスがすべて止まってしまうからです。

高負荷な環境やユーザー体験が重視されるアプリケーションでは、fs.promises.readFilefs.readFile(コールバック版)を使用するのが鉄則です。

比較表:同期 vs 非同期

特性同期 (readFileSync)非同期 (readFile / fs.promises)
実行順序記述した順に上から実行される完了を待たずに次の処理へ進む
コードの簡潔さ非常にシンプルPromiseやasync/awaitが必要
スレッドへの影響メインスレッドをブロックするスレッドを解放し他の処理を継続できる
推奨される場面起動時の設定、CLIツール、スクリプトAPIサーバー、高トラフィックなWebアプリ

実践的な実装例:JSONファイルの同期読み込み

Node.js開発で頻繁に発生するのが、JSON形式の設定ファイルを読み込んでオブジェクトとして扱う処理です。

同期処理を用いると、非常にスマートに記述できます。

JavaScript
import { readFileSync } from 'node:fs';

/**
 * 設定ファイルを読み込む関数
 * @param {string} filePath 
 * @returns {object}
 */
function loadConfig(filePath) {
    try {
        // 同期的に読み込み
        const rawData = readFileSync(filePath, 'utf8');
        // JSONとしてパース
        return JSON.parse(rawData);
    } catch (error) {
        console.error('設定ファイルの読み込みに失敗しました:', error.message);
        // 失敗した場合はデフォルト値を返すか、プロセスを終了させる
        process.exit(1);
    }
}

const config = loadConfig('./config.json');
console.log('設定の読み込みが完了しました。ポート番号:', config.port);

このように、同期処理はtry…catch構文によるエラーハンドリングと非常に相性が良いのが特徴です。

同期読み込みにおけるエラーハンドリングの重要性

非同期処理(コールバック形式)ではエラーは引数として渡されますが、同期処理では例外(Exception)としてスローされます。

ファイルが存在しない場合や、権限不足でアクセスできない場合にプログラムがクラッシュするのを防ぐため、必ず例外処理を組み込む必要があります。

try…catchによる例外捕捉

同期メソッドを使用する際は、常にtry…catchブロックで囲む習慣をつけましょう。

JavaScript
import { readFileSync } from 'node:fs';

try {
    const data = readFileSync('non-existent-file.txt', 'utf8');
} catch (err) {
    if (err.code === 'ENOENT') {
        console.error('エラー:ファイルが見つかりません');
    } else {
        console.error('予期せぬエラーが発生しました:', err.message);
    }
}

エラーオブジェクトに含まれるcodeプロパティを確認することで、ファイル不在(ENOENT)や権限エラー(EACCES)など、原因に応じた適切な処理が可能になります。

パフォーマンスとメモリへの配慮

同期的な読み込みには、実行時間のブロック以外にも注意すべき点があります。

それは、読み込んだファイルの内容をすべてメモリ上に展開するという点です。

大きなファイルの読み込みに注意

数ギガバイトに及ぶような大容量のファイルをreadFileSyncで読み込もうとすると、Node.jsのヒープメモリを圧迫し、JavaScript heap out of memoryエラーを引き起こす可能性があります。

巨大なファイルを扱う場合は、同期・非同期に関わらず、fs.createReadStreamなどのストリーム処理を検討してください。

ストリームを使用すれば、ファイルを分割して少しずつ処理できるため、メモリ消費を最小限に抑えることができます。

2026年現在のNode.js環境における推奨事項

現在のNode.js環境(Node.js 22や24以降)では、ES Modules(ESM)が標準的な書き方となっています。

従来のrequire('fs')ではなく、import { readFileSync } from 'node:fs'のようにnode:スキームを明示したインポートが推奨されています。

これにより、組み込みモジュールであることを明示し、サードパーティ製のパッケージとの混同を避けることができます。

パス指定におけるセキュリティの考慮

ユーザーからの入力をそのままファイルパスとしてreadFileSyncに渡すことは極めて危険です。

ディレクトリトラバーサル攻撃により、システム上の意図しない重要ファイル(パスワードファイルなど)が読み取られるリスクがあるからです。

パスを扱う際は、必ずnode:pathモジュールのpath.resolvepath.joinを使用し、適切なサニタイズを行ってください。

まとめ

Node.jsでのファイル同期読み込みは、そのシンプルさから初期化処理やスクリプト作成において非常に強力なツールとなります。

fs.readFileSyncを正しく使い、適切なエンコーディング指定とエラーハンドリングを行うことで、堅牢なプログラムを作成することが可能です。

しかし、メインスレッドをブロックするという特性を忘れず、「いつ同期処理を使うべきか」を常に意識することが重要です。

サーバーサイドの動的なリクエスト処理では非同期(Promises)を、起動時やツール制作では同期(Sync)を選択するという使い分けを徹底しましょう。

最新のNode.jsの仕様に合わせたセキュアで効率的な実装を心がけ、最適なアーキテクチャを選択してください。