Node.jsを用いたアプリケーション開発において、コードを機能ごとに分割して管理する「モジュール」の理解は欠かせません。

近年のNode.jsでは、従来のCommonJSに加えてJavaScriptの標準仕様であるES Modulesが主流となり、開発の選択肢が広がっています。

本記事では、Node.jsのモジュールの基礎知識から、2つの主要なモジュールシステムの仕組み、そして2026年現在の開発現場で求められる適切な使い分けについて詳しく解説します。

Node.jsにおけるモジュールの基本概念

Node.jsにおけるモジュールとは、プログラムを構成する独立した機能の塊を指します。

一つのファイルにすべての処理を記述すると、コードの可読性が低下し、メンテナンスが非常に困難になります。

モジュールシステムを利用することで、特定の機能(例えばデータベース接続や数値計算など)を別ファイルに切り出し、必要な場所で呼び出すことが可能になります。

このようにコードを部品化することで、再利用性が高まり、チーム開発においても効率的な分業が実現します。

Node.jsは誕生当初から独自のモジュールシステムを採用してきましたが、現在ではWebブラウザとの互換性を持つ標準仕様への移行が進んでいます。

モジュールの3つの分類

Node.jsで扱うモジュールは、大きく分けて以下の3つのカテゴリに分類されます。

1. コアモジュール

コアモジュールは、Node.jsをインストールした際に標準で組み込まれているモジュールです。

ファイル操作を行うfsや、パスを扱うpath、ネットワーク通信を行うhttpなどが代表的です。

これらは追加のインストール作業なしに、すぐに呼び出して使用できるのが特徴です。

2. 外部モジュール(サードパーティモジュール)

外部モジュールは、npm(Node Package Manager)を通じてインストールするパッケージです。

世界中の開発者が公開した便利なライブラリをプロジェクトに取り入れることができます。

2026年現在も、膨大な数のパッケージがエコシステムを支えており、開発速度を劇的に向上させています。

3. ローカルモジュール

ローカルモジュールは、開発者自身がプロジェクト内で作成するJavaScriptファイルです。

アプリケーション固有のビジネスロジックや共通関数をファイルごとに分割して管理します。

CommonJS(CJS)の仕組みと使い方

CommonJSは、Node.jsの黎明期から採用されてきた伝統的なモジュールシステムです。

多くの既存プロジェクトや古いライブラリでは、現在もこの形式が広く使われています。

requireとmodule.exports

CommonJSでは、モジュールの書き出しにmodule.exports、読み込みにrequire関数を使用します。

以下のコード例は、計算機能をモジュール化し、別のファイルで読み込む基本的なパターンを示しています。

JavaScript
// math.js (書き出し側)
const add = (a, b) => a + b;

// module.exportsを使用して外部に公開する
module.exports = {
  add
};
JavaScript
// app.js (読み込み側)
// requireを使用してモジュールを読み込む
const math = require('./math.js');

const result = math.add(5, 10);
console.log(`合計: ${result}`);
実行結果
合計: 15

CommonJSの特徴は、モジュールの読み込みが同期的(Synchronous)に行われる点にあります。

そのため、スクリプトの実行中に動的にモジュールをロードすることが容易ですが、ブラウザ環境での動作には適していません。

ES Modules(ESM)の仕組みと使い方

ES Modules(ESM)は、ECMAScriptの標準として定義されたモジュールシステムです。

Node.js v12頃から本格的なサポートが始まり、現在ではモダンな開発における標準となっています。

importとexport

ESMでは、モジュールの書き出しにexport、読み込みにimportキーワードを使用します。

拡張子は.mjsとするか、package.json"type": "module"を設定することで有効になります。

JavaScript
// utils.js (書き出し側)
export const multiply = (a, b) => a * b;

export default function greeting(name) {
  return `こんにちは、${name}さん`;
}
JavaScript
// main.js (読み込み側)
import greeting, { multiply } from './utils.js';

console.log(multiply(4, 3));
console.log(greeting('Node.jsユーザー'));
実行結果
12
こんにちは、Node.jsユーザーさん

ESMは、静的な解析が可能であるため、ビルドツールによる最適化(ツリーシェイキングなど)を受けやすいというメリットがあります。

また、ブラウザとNode.jsで同じ構文を使用できるため、コードの共有が非常にスムーズになります。

CommonJSとES Modulesの主な違い

これら2つのシステムには、構文以外にも重要な動作の違いが存在します。

以下の表で、主な相違点を整理しました。

比較項目CommonJS (CJS)ES Modules (ESM)
構文require / module.exportsimport / export
ロードの性質同期的非同期的(静的)
拡張子.js / .cjs.js / .mjs
__dirname, __filename使用可能使用不可(代替手段が必要)
トップレベルAwait不可可能

動作環境と特殊変数の違い

CommonJSでは、実行ファイルがあるディレクトリパスを取得する__dirnameや、ファイル名を取得する__filenameが自動的に提供されます。

しかし、ES Modulesではこれらの変数が存在せず、代わりにimport.meta.urlを使用してパスを解決する必要があります。

これは、ESMがネットワーク越しにファイルをロードする可能性を考慮した設計になっているためです。

2026年におけるモジュールの使い分け指針

現在、新規プロジェクトを開始する場合は、原則としてES Modules(ESM)を採用することが推奨されます。

フロントエンドとバックエンドでコードを共有する機会が増えているため、共通の規格を用いるメリットが非常に大きいからです。

ESMを選択すべきケース

最新のライブラリやフレームワーク(Next.js, Viteなど)を利用する場合は、ESMがデフォルトとなります。

また、将来的なメンテナンス性や、最新のJavaScript仕様をフルに活用したい場合もESM一択です。

特に「トップレベルAwait」を利用できる点は、非同期処理が多いサーバーサイド開発において大きな強みとなります。

CommonJSを選択・維持すべきケース

長年運用されているレガシーなシステムや、ESMに未対応の古いライブラリに強く依存している場合は、CommonJSを維持する必要があります。

また、AWS Lambdaなどのサーバーレス環境において、既存のランタイム設定がCommonJSに最適化されている場合も、慎重な検討が必要です。

ただし、これらの環境でも徐々にESMへの移行が進んでいるため、段階的なアップデートを計画することが望ましいでしょう。

パッケージ管理とモジュールの解決

Node.jsは、モジュールを探す際にnode_modulesディレクトリを再帰的に探索します。

この仕組みにより、開発者は複雑なパス指定をすることなく、ライブラリ名だけで機能を呼び出すことができます。

2026年現在の環境では、package.json内のexportsフィールドを適切に設定することで、CJSとESMの両方をサポートする「デュアルパッケージ」の公開も容易になっています。

JSON
{
  "name": "my-library",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.cjs"
    }
  }
}

このような設定を行うことで、利用側の環境に合わせて最適な形式のモジュールが自動的に選択されます。

ライブラリ作成者にとっては、より多くのユーザーに安定したコードを提供する重要なテクニックです。

まとめ

Node.jsのモジュールシステムは、コードの整理と再利用を支える非常に強力な仕組みです。

歴史あるCommonJSと、標準仕様であるES Modulesの2つを理解することは、現代のNode.jsエンジニアにとって必須のスキルと言えます。

「新しく作るならESM、既存資産を守るならCJS」という方針を基本にしつつ、両者の違いを正しく把握しておきましょう。

特にpackage.jsonの設定や、非同期的な読み込みの特性に注目することで、トラブルの少ない開発が可能になります。

進化を続けるNode.jsのエコシステムにおいて、適切なモジュール管理を行い、堅牢で拡張性の高いアプリケーションを目指してください。