Node.js
エンジニアのためのWebチートシート
Node.jsはJavaScriptのサーバーサイドランタイムです。 モジュールシステム、ファイルシステム、HTTP、ストリーム、プロセス管理、イベント、非同期パターン、crypto、テストランナーなどの基本をチートシートにまとめました。
モジュールシステム
ES Modules(推奨)
モダンなモジュールシステムです。node: プレフィックスでコアモジュールを明示できます。
// node: prefix (recommended in v22+) import { readFile } from 'node:fs/promises'; import { createServer } from 'node:http'; import path from 'node:path'; // Named export export function add(a, b) { return a + b; } export const PI = 3.14; // Default export export default class MyService {} // Dynamic import const mod = await import('./module.js'); // Import JSON (with assertion) import config from './config.json' with { type: 'json' };
CommonJS
従来のモジュールシステムです。既存コードとの互換性があります。
// Import const fs = require('fs'); const { readFile } = require('fs'); // Export module.exports = { add, subtract }; module.exports = class MyClass {}; exports.helper = function() {}; // __dirname, __filename (CJS only) console.log(__dirname); console.log(__filename); // ESM equivalent import { fileURLToPath } from 'node:url'; import { dirname } from 'node:path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);
package.json 基本
プロジェクト設定とスクリプト定義の基本です。
{ "name": "my-app", "version": "1.0.0", "type": "module", "main": "dist/index.js", "exports": { ".": { "import": "./dist/index.mjs", "require": "./dist/index.cjs" } }, "scripts": { "dev": "node --watch --env-file=.env app.js", "start": "node app.js", "test": "node --test" }, "engines": { "node": ">=22.0.0" } }
セマンティックバージョニング
| 記号 | 意味 |
|---|---|
| ^1.2.3 | マイナー+パッチ更新を許可 (>=1.2.3 <2.0.0) |
| ~1.2.3 | パッチ更新のみ許可 (>=1.2.3 <1.3.0) |
| * | 全バージョン (任意のバージョン) |
ファイルシステム (fs)
fs/promises(推奨)
Promise ベースのファイル操作APIです。async/await で使用します。
import { readFile, writeFile, appendFile, mkdir, readdir, rm, stat, rename, copyFile } from 'node:fs/promises'; const data = await readFile('file.txt', 'utf8'); await writeFile('out.txt', 'Hello', 'utf8'); await appendFile('log.txt', 'entry\n'); await mkdir('path/to/dir', { recursive: true });const files = await readdir('.', { withFileTypes: true }); const names = files .filter(f => f.isFile()) .map(f => f.name); await rm('file.txt'); await rm('dir', { recursive: true, force: true });const s = await stat('file.txt'); s.isFile(); // true/false s.isDirectory(); // true/false s.size; // bytes s.mtime; // last modified await rename('old.txt', 'new.txt'); await copyFile('src.txt', 'dest.txt');
同期API & ストリーム
同期APIはCLIツールに、ストリームは大きなファイルの処理に適しています。
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs'; // Sync read/write const data = readFileSync('file.txt', 'utf8'); writeFileSync('out.txt', data); // Check existence if (existsSync('config.json')) { /* ... */ } // Sync mkdir mkdirSync('dir', { recursive: true });// Stream API (large files) import { createReadStream, createWriteStream } from 'node:fs'; const read = createReadStream('large.txt', { encoding: 'utf8' }); const write = createWriteStream('output.txt'); read.pipe(write); // Async iterator for await (const chunk of read) { process.stdout.write(chunk); }
パス & URL
path モジュール
ファイルパスの操作ユーティリティです。
import path from 'node:path'; path.join('src', 'utils', 'index.js'); // → 'src/utils/index.js' path.resolve('src', 'file.js'); // → '/Users/.../project/src/file.js' path.dirname('/etc/nginx/nginx.conf'); // '/etc/nginx' path.basename('/etc/nginx/nginx.conf'); // 'nginx.conf' path.extname('index.html'); // '.html'path.parse('/home/user/file.txt'); // { root:'/', dir:'/home/user', // base:'file.txt', ext:'.txt', name:'file' } path.relative('/data/test', '/data/impl'); // → '../impl' path.normalize('/foo/bar//baz/./quux/..'); // → '/foo/bar/baz' path.sep; // '/' or '\\' path.delimiter; // ':' or ';'
URL & URLSearchParams
WHATWG URL APIによるURL解析と操作です。
const url = new URL( 'https://example.com:8080/path?q=test#hash' ); url.hostname; // 'example.com' url.port; // '8080' url.pathname; // '/path' url.hash; // '#hash' url.origin; // 'https://example.com:8080' url.searchParams.get('q'); // 'test'const params = new URLSearchParams({ page: '1', limit: '10' }); params.toString(); // 'page=1&limit=10' params.append('sort', 'name'); params.delete('limit'); params.has('page'); // true new URL('/api/users', 'https://example.com').href; // → 'https://example.com/api/users'
HTTP サーバー & Fetch
HTTPサーバー
http モジュールによるHTTPサーバーの作成です。
import { createServer } from 'node:http'; const server = createServer((req, res) => { const { method, url } = req; if (method === 'GET' && url === '/api/hello') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ msg: 'Hello' })); return; } res.writeHead(404); res.end('Not Found'); }); server.listen(3000);// Read POST body const server = createServer((req, res) => { if (req.method === 'POST') { let body = ''; req.on('data', chunk => body += chunk); req.on('end', () => { const data = JSON.parse(body); res.writeHead(201, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(data)); }); } });
Fetch API(v22+ 安定)
ネイティブのFetch APIでHTTPリクエストを行います。
// GET const res = await fetch('https://api.example.com/data'); const json = await res.json(); // POST const res2 = await fetch('https://api.example.com', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'test' }), });// Timeout const res = await fetch(url, { signal: AbortSignal.timeout(5000), }); // AbortController const ctrl = new AbortController(); setTimeout(() => ctrl.abort(), 10000); await fetch(url, { signal: ctrl.signal });// Response properties & methods res.ok; // status 200-299 res.status; // 200 res.statusText; // 'OK' res.headers.get('content-type'); await res.json(); await res.text(); await res.arrayBuffer();
ストリーム
ストリームタイプ
| タイプ | 説明 | 例 |
|---|---|---|
| Readable | データを生成するストリーム | fs.createReadStream, http req |
| Writable | データを消費するストリーム | fs.createWriteStream, http res |
| Transform | 入力を変換して出力するストリーム | zlib, crypto |
| Duplex | 独立した入出力を持つストリーム | TCP socket |
pipeline & 操作
ストリームの接続と操作方法です。pipeline() がモダンな推奨パターンです。
import { pipeline } from 'node:stream/promises'; import { createReadStream, createWriteStream } from 'node:fs'; import { createGzip } from 'node:zlib'; await pipeline( createReadStream('input.txt'), createGzip(), createWriteStream('input.txt.gz') );import { Transform } from 'node:stream'; const upper = new Transform({ transform(chunk, enc, cb) { this.push(chunk.toString().toUpperCase()); cb(); } });// Readable events readable.on('data', (chunk) => {}); readable.on('end', () => {}); readable.on('error', (err) => {}); // Writable events writable.write(chunk); writable.end(); writable.on('finish', () => {}); writable.on('drain', () => {});
プロセス & 子プロセス
process オブジェクト
環境変数、引数、メモリ、ディレクトリなどのプロセス情報です。
process.env.NODE_ENV; // 'production' process.env.PORT; // '3000' process.argv; // [node, script, ...args] process.argv.slice(2); // user args only process.pid; // Process ID process.version; // 'v22.x.x' process.platform; // 'darwin'|'linux'|'win32' process.arch; // 'x64'|'arm64' process.cwd(); // current dirconst { rss, heapUsed, heapTotal } = process.memoryUsage(); const start = process.hrtime.bigint(); const ns = process.hrtime.bigint() - start; console.log(`${ns / 1000000n}ms`); process.stdout.write('output\n'); process.stderr.write('error\n'); process.exit(0); // success process.exitCode = 1; // deferred exit
子プロセス (child_process)
exec, spawn, fork による外部コマンド実行です。
import { exec, execSync, spawn, fork } from 'node:child_process'; exec('ls -la', (err, stdout, stderr) => { if (err) return console.error(err); console.log(stdout); }); const out = execSync('ls', { encoding: 'utf8' });// spawn: streaming output const child = spawn('find', ['.', '-type', 'f']); child.stdout.on('data', d => console.log(`${d}`)); child.on('close', code => console.log(code)); // fork: subprocess with IPC const worker = fork('./worker.js'); worker.send({ task: 'process', data: [1,2,3] }); worker.on('message', r => console.log(r));spawn('cmd', args, { cwd: '/path/to/dir', env: { ...process.env }, stdio: 'inherit', shell: true, timeout: 5000, });
シグナル & シャットダウン
プロセスシグナルの処理とグレースフルシャットダウンです。
process.on('SIGINT', () => { gracefulShutdown('SIGINT'); }); process.on('SIGTERM', () => { gracefulShutdown('SIGTERM'); }); function gracefulShutdown(signal) { console.log(`Received ${signal}`); server.close(() => process.exit(0)); setTimeout(() => process.exit(1), 10000); }process.on('uncaughtException', (err) => { console.error('Uncaught:', err); process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('Unhandled:', reason); });
イベント & 非同期パターン
EventEmitter
イベント駆動のパブリッシュ/サブスクライブパターンです。
import { EventEmitter } from 'node:events'; const emitter = new EventEmitter(); emitter.on('data', (payload) => { console.log(payload); }); emitter.once('init', () => { console.log('Once only'); }); emitter.emit('data', { id: 1 });const handler = (d) => {}; emitter.on('event', handler); emitter.removeListener('event', handler); emitter.removeAllListeners('event'); emitter.setMaxListeners(20); class MyService extends EventEmitter { process(data) { this.emit('start', data); this.emit('done', result); } }
非同期パターン
Promise.all, Promise.allSettled, util.promisify のパターンです。
import { promisify } from 'node:util'; import { exec } from 'node:child_process'; const execAsync = promisify(exec); const { stdout } = await execAsync('ls -la'); const [config, users] = await Promise.all([ readFile('config.json', 'utf8'), fetch('/api/users').then(r => r.json()), ]);const results = await Promise.allSettled([ fetch(url1), fetch(url2), fetch(url3) ]); results.forEach(r => { if (r.status === 'fulfilled') console.log(r.value); else console.error(r.reason); });try { const data = await readFile('cfg.json', 'utf8'); const config = JSON.parse(data); } catch (err) { if (err.code === 'ENOENT') console.error('File not found'); else throw err; }
一般的なエラーコード
| コード | 意味 |
|---|---|
| ENOENT | ファイル/ディレクトリが存在しない |
| EACCES | 権限不足 |
| EADDRINUSE | ポートが使用中 |
| ECONNREFUSED | 接続拒否 |
| ETIMEDOUT | タイムアウト |
crypto & Buffer
crypto モジュール
ハッシュ、HMAC、暗号化、ランダム値生成の基本操作です。
import { createHash, createHmac, randomBytes, randomUUID, createCipheriv, createDecipheriv, scryptSync, timingSafeEqual } from 'node:crypto'; createHash('sha256').update('hello').digest('hex'); createHmac('sha256', secretKey) .update('message').digest('hex'); const token = randomBytes(32).toString('hex'); const uuid = randomUUID();// AES-256-GCM encryption const key = scryptSync('password', 'salt', 32); const iv = randomBytes(16); const cipher = createCipheriv( 'aes-256-gcm', key, iv ); let enc = cipher.update('secret', 'utf8', 'hex'); enc += cipher.final('hex'); const tag = cipher.getAuthTag();// Decryption const decipher = createDecipheriv( 'aes-256-gcm', key, iv ); decipher.setAuthTag(tag); let dec = decipher.update(enc, 'hex', 'utf8'); dec += decipher.final('utf8'); timingSafeEqual( Buffer.from(a), Buffer.from(b) );
Buffer & エンコーディング
バイナリデータの操作とエンコーディング変換です。
Buffer.from('Hello', 'utf8'); Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]); Buffer.alloc(10); // zero-filled Buffer.allocUnsafe(10); // uninitialized const buf = Buffer.from('Hello'); buf.toString('base64'); // 'SGVsbG8=' buf.toString('hex'); // '48656c6c6f' buf.toString('utf8'); // 'Hello'Buffer.from('SGVsbG8=', 'base64') .toString('utf8'); // 'Hello' buf.length; // byte length buf.slice(0, 3); // sub-buffer Buffer.concat([buf1, buf2]);// concatenate buf.copy(target); // copy buf.fill(0); // fill all bytes Buffer.byteLength('Hello'); // 5 Buffer.isBuffer(obj); // true/false
テスト & モダン機能
ビルトインテストランナー
node:test モジュールによるネイティブテスト(v22+)です。
import { test, describe, it, before, after } from 'node:test'; import assert from 'node:assert/strict'; describe('Math', () => { test('adds numbers', () => { assert.strictEqual(1 + 2, 3); }); test('async test', async () => { const res = await fetchData(); assert.deepStrictEqual(res, expected); }); test('throws', () => { assert.throws(() => { throw new Error('fail'); }, { message: 'fail' }); }); });# Run tests node --test node --test --watch node --test **/*.test.js
v22+ モダン機能
Node.js v22以降の新機能です。
# Watch mode (auto-restart) node --watch app.js # .env file loading (no dotenv needed) node --env-file=.env app.js # Run package.json scripts node --run dev # TypeScript stripping (v22.6+) node index.ts # Permission model node --experimental-permission \ --allow-fs-read=/data app.jsimport { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; if (isMainThread) { const worker = new Worker( new URL('./worker.js', import.meta.url), { workerData: { input: data } } ); worker.on('message', r => console.log(r)); } else { const result = compute(workerData.input); parentPort.postMessage(result); }performance.mark('start'); performance.mark('end'); performance.measure('op', 'start', 'end'); const [m] = performance.getEntriesByName('op'); console.log(`${m.duration}ms`);