Node.js 22がLTSに昇格 - require(ESM)と新機能まとめ

2025.12.02

公式ドキュメント

Node.js 22 LTSの概要

Node.js 22が2024年10月にLTS(長期サポート)に昇格しました。コードネーム「Jod」として、2027年2月まで積極的なサポートが提供されます。

バージョンリリースLTS開始終了
Node.js 182022/042022/102025/04
Node.js 202023/042023/102026/04
Node.js 222024/042024/102027/04
Node.js 242025/042025/102028/04

ポイント: 本番環境では必ずLTSバージョンを使用しましょう。Node.js 22は2027年4月までサポートされます。

require()でのESMサポート

Node.js 22の最大の目玉は、CommonJSからrequire()でESモジュールを読み込めるようになったことです。

// ESM パッケージ (package.json: "type": "module")
// lib.mjs
export const greeting = 'Hello';
export function sayHello(name) {
 return `${greeting}, ${name}!`;
}
export default { greeting, sayHello };

// CommonJSから読み込み可能に
// app.cjs
const { greeting, sayHello } = require('./lib.mjs');
console.log(sayHello('World')); // Hello, World!

// デフォルトエクスポートも利用可能
const lib = require('./lib.mjs');
console.log(lib.default.greeting); // Hello

実験的フラグからの昇格

# Node.js 22以前(実験的機能)
node --experimental-require-module app.js

# Node.js 22.12.0以降(デフォルトで有効)
node app.js

# 無効化する場合
node --no-experimental-require-module app.js

注意: トップレベルawaitを含むESモジュールはrequire()で読み込めません。ERR_REQUIRE_ASYNC_MODULEエラーが発生します

注意点

// トップレベルawaitを含むESMはrequire不可
// async-lib.mjs
const data = await fetch('https://api.example.com/data');
export const config = await data.json();

// これはエラーになる
try {
 require('./async-lib.mjs'); // ERR_REQUIRE_ASYNC_MODULE
} catch (e) {
 console.error('トップレベルawaitを含むモジュールはrequireできません');
}

WebSocketクライアント(標準搭載)

// Node.js 22以降、外部ライブラリ不要でWebSocket利用可能
const ws = new WebSocket('wss://echo.websocket.org');

ws.addEventListener('open', () => {
 console.log('接続完了');
 ws.send('Hello, WebSocket!');
});

ws.addEventListener('message', (event) => {
 console.log('受信:', event.data);
});

ws.addEventListener('close', () => {
 console.log('切断');
});

ws.addEventListener('error', (error) => {
 console.error('エラー:', error);
});

HTTPサーバーとの統合

import { createServer } from 'http';
import { WebSocketServer } from 'ws';

const server = createServer();
const wss = new WebSocketServer({ server });

wss.on('connection', (ws, request) => {
 console.log('新規接続:', request.url);

 ws.on('message', (message) => {
 // ブロードキャスト
 wss.clients.forEach((client) => {
 if (client.readyState === WebSocket.OPEN) {
 client.send(message.toString());
 }
 });
 });
});

server.listen(8080, () => {
 console.log('WebSocket server running on ws://localhost:8080');
});

V8エンジン 12.4

Node.js 22はV8 12.4を搭載し、最新のJavaScript機能が利用可能です。

Array.fromAsync

// 非同期イテラブルから配列を作成
async function* asyncGenerator() {
 yield 1;
 yield 2;
 yield 3;
}

const arr = await Array.fromAsync(asyncGenerator());
console.log(arr); // [1, 2, 3]

// Promiseの配列も処理可能
const promises = [
 fetch('/api/user/1'),
 fetch('/api/user/2'),
 fetch('/api/user/3'),
];

const responses = await Array.fromAsync(promises, (p) => p.then((r) => r.json()));

Set メソッドの追加

const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);

// 和集合
console.log(a.union(b)); // Set {1, 2, 3, 4, 5, 6}

// 積集合
console.log(a.intersection(b)); // Set {3, 4}

// 差集合
console.log(a.difference(b)); // Set {1, 2}

// 対称差
console.log(a.symmetricDifference(b)); // Set {1, 2, 5, 6}

// 部分集合チェック
console.log(new Set([1, 2]).isSubsetOf(a)); // true
console.log(a.isSupersetOf(new Set([1, 2]))); // true

// 素集合チェック
console.log(a.isDisjointFrom(new Set([7, 8]))); // true

Iterator Helpers

// イテレータに対する変換操作
function* numbers() {
 yield 1;
 yield 2;
 yield 3;
 yield 4;
 yield 5;
}

// map, filter, take などが利用可能(遅延評価)
const result = numbers()
 .filter((n) => n % 2 === 0)
 .map((n) => n * 2)
 .take(2)
 .toArray();

console.log(result); // [4, 8]

// drop: 先頭をスキップ
const skipped = numbers().drop(2).toArray();
console.log(skipped); // [3, 4, 5]

// flatMap
function* nested() {
 yield [1, 2];
 yield [3, 4];
}
const flat = nested()
 .flatMap((arr) => arr)
 .toArray();
console.log(flat); // [1, 2, 3, 4]

// find
const found = numbers().find((n) => n > 3);
console.log(found); // 4

// some / every
console.log(numbers().some((n) => n > 3)); // true
console.log(numbers().every((n) => n > 0)); // true

SQLite組み込み(実験的)

import { DatabaseSync } from 'node:sqlite';

// インメモリデータベース
const db = new DatabaseSync(':memory:');

// テーブル作成
db.exec(`
 CREATE TABLE users (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 name TEXT NOT NULL,
 email TEXT UNIQUE
 )
`);

// データ挿入
const insert = db.prepare('INSERT INTO users (name, email) VALUES (?, ?)');
insert.run('Alice', 'alice@example.com');
insert.run('Bob', 'bob@example.com');

// クエリ実行
const select = db.prepare('SELECT * FROM users WHERE name = ?');
const user = select.get('Alice');
console.log(user); // { id: 1, name: 'Alice', email: 'alice@example.com' }

// 全件取得
const all = db.prepare('SELECT * FROM users').all();
console.log(all);

// トランザクション
const transfer = db.transaction((from, to, amount) => {
 db.prepare('UPDATE accounts SET balance = balance - ? WHERE id = ?').run(amount, from);
 db.prepare('UPDATE accounts SET balance = balance + ? WHERE id = ?').run(amount, to);
});

transfer(1, 2, 100);

パフォーマンス改善

項目Node.js 20Node.js 22改善率
起動時間 (Hello World)120ms85ms29% faster
HTTPサーバー (req/sec)45,00052,00015% faster
ファイルI/O (1000ファイル読み込み)280ms230ms18% faster
ストリーム処理320ms250ms22% faster

Watch Mode の安定化

# ファイル変更を監視して自動再起動
node --watch server.js

# 特定のパスのみ監視
node --watch-path=./src --watch-path=./config server.js

# 監視除外
node --watch --watch-preserve-output server.js
// プログラム的に監視状態を確認
import { watch } from 'node:fs/promises';

const ac = new AbortController();
const { signal } = ac;

(async () => {
 const watcher = watch('./src', { recursive: true, signal });
 for await (const event of watcher) {
 console.log(`${event.eventType}: ${event.filename}`);
 }
})();

// 監視停止
// ac.abort();

Glob パターンのネイティブサポート

import { glob, globSync } from 'node:fs';

// 非同期API
const files = await glob('**/*.js', {
 cwd: './src',
 ignore: ['node_modules/**'],
});
console.log(files);

// 同期API
const syncFiles = globSync('**/*.{ts,tsx}');
console.log(syncFiles);

// fsモジュール経由でも利用可能
import { promises as fs } from 'node:fs';
const matches = await fs.glob('src/**/*.test.ts');

実践メモ: --env-fileフラグでdotenvライブラリなしに.envファイルを直接読み込めます。複数ファイルの指定も可能です。

環境変数ファイルの読み込み

# .envファイルを自動読み込み
node --env-file=.env app.js

# 複数ファイル対応
node --env-file=.env --env-file=.env.local app.js
# .env
DATABASE_URL=postgresql://localhost:5432/mydb
API_KEY=YOUR_API_KEY
NODE_ENV=development
// 環境変数にアクセス
console.log(process.env.DATABASE_URL);
console.log(process.env.API_KEY);

テストランナーの改善

// test/user.test.js
import { describe, it, before, after, mock } from 'node:test';
import assert from 'node:assert/strict';

describe('User Service', () => {
 let mockDb;

 before(() => {
 mockDb = mock.fn(() => ({ id: 1, name: 'Test User' }));
 });

 after(() => {
 mock.reset();
 });

 it('should create a user', async () => {
 const user = await createUser({ name: 'Test User' });
 assert.strictEqual(user.name, 'Test User');
 });

 it('should validate email', () => {
 assert.throws(() => createUser({ email: 'invalid' }), /Invalid email/);
 });

 // スナップショットテスト
 it('should match snapshot', async (t) => {
 const user = await getUser(1);
 t.assert.snapshot(user);
 });
});
# テスト実行
node --test

# カバレッジ付き
node --test --experimental-test-coverage

# 特定ファイルのみ
node --test test/user.test.js

移行ガイド

# バージョン確認
node --version

# nvm使用の場合
nvm install 22
nvm use 22
nvm alias default 22

# 依存関係の互換性確認
npx npm-check-updates --target minor

# package.json のエンジン指定
# "engines": { "node": ">=22" }

まとめ

このニュースは、今すぐ全員が対応すべき話か、学習や新規開発で前提を更新しておく話かを分けて読むことが重要です。まずは公式情報で対象バージョンと影響範囲を確認し、自分の環境に関係する部分から見直してください。

参考リソース

← 一覧に戻る
PR
PR
PR
PR