🟢
Node.js言語 サンプルコード
文法解説で学んだ内容を組み合わせた、実践的なサンプルプログラムを3本紹介します。難易度が上がる順に、①簡易TODO APIサーバー、②非同期ファイル読み書きを使った家計簿ツール、③ストリームとイベントを使ったログ集計ツールを扱います。
サンプル1:簡易TODO APIサーバー(httpモジュール)
組み込みのhttpモジュールだけで、メモリ上のデータを操作するシンプルなREST風APIを作ります。req.urlとreq.methodで手動ルーティングし、POSTのリクエストボディはストリームのdata / endイベントで少しずつ受け取って組み立てます。JSONのパースやレスポンスの組み立てでtry/catchによるエラーハンドリングも行っています。
SAMPLE PROGRAM
const http = require("http");
let todos = [
{ id: 1, title: "牛乳を買う", done: false },
{ id: 2, title: "Node.jsの復習", done: false },
];
let nextId = 3;
function sendJson(res, statusCode, data) {
const body = JSON.stringify(data);
res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
res.end(body);
}
// POSTのリクエストボディをストリームで受け取って文字列に組み立てる
function readBody(req) {
return new Promise((resolve, reject) => {
let raw = "";
req.on("data", (chunk) => {
raw += chunk; // チャンクを少しずつ受信
});
req.on("end", () => resolve(raw));
req.on("error", reject);
});
}
const server = http.createServer(async (req, res) => {
try {
if (req.method === "GET" && req.url === "/todos") {
return sendJson(res, 200, todos);
}
if (req.method === "POST" && req.url === "/todos") {
const raw = await readBody(req);
const input = JSON.parse(raw); // 不正なJSONならcatchへ
const todo = { id: nextId++, title: input.title, done: false };
todos.push(todo);
return sendJson(res, 201, todo);
}
sendJson(res, 404, { error: "Not Found" });
} catch (err) {
sendJson(res, 400, { error: "リクエストの処理に失敗しました: " + err.message });
}
});
server.listen(3000, () => {
console.log("http://localhost:3000/todos で待ち受け中");
});
サンプル2:非同期ファイル読み書きを使う家計簿ツール
fs/promisesとasync/awaitを使い、JSONファイルに保存された家計簿データを読み書きします。ファイルがまだ存在しない場合のエラー(err.code === "ENOENT")を判別して初期データを作るなど、実務でもよくあるエラーハンドリングのパターンを含んでいます。
SAMPLE PROGRAM
const fs = require("fs/promises");
const FILE_PATH = "./ledger.json";
async function loadLedger() {
try {
const raw = await fs.readFile(FILE_PATH, "utf-8");
return JSON.parse(raw);
} catch (err) {
if (err.code === "ENOENT") {
// ファイルがまだない場合は空のデータから開始
return { entries: [] };
}
throw err; // それ以外のエラーは呼び出し元に伝える
}
}
async function saveLedger(ledger) {
const json = JSON.stringify(ledger, null, 2);
await fs.writeFile(FILE_PATH, json, "utf-8");
}
async function addEntry(title, amount) {
const ledger = await loadLedger();
ledger.entries.push({ title, amount, date: new Date().toISOString() });
await saveLedger(ledger);
return ledger;
}
function summarize(ledger) {
return ledger.entries.reduce((total, e) => total + e.amount, 0);
}
(async () => {
try {
await addEntry("ランチ", -800);
await addEntry("給料", 250000);
const ledger = await loadLedger();
console.log("収支合計:", summarize(ledger), "円");
} catch (err) {
console.error("家計簿の処理でエラーが発生しました:", err.message);
}
})();
サンプル3:ストリームとEventEmitterを使うログ集計ツール
大きなログファイルを一度に全部メモリへ読み込まず、readlineとcreateReadStreamを組み合わせて1行ずつ処理します。自作のEventEmitterを使って「一定件数ごとの進捗」「完了」「エラー」を通知するイベント駆動の設計にしており、呼び出し側はon()でイベントを購読するだけで進捗を受け取れます。
SAMPLE PROGRAM
const fs = require("fs");
const readline = require("readline");
const { EventEmitter } = require("events");
class LogAnalyzer extends EventEmitter {
constructor(keyword) {
super();
this.keyword = keyword;
}
analyze(filePath) {
let lineCount = 0;
let matchCount = 0;
const stream = fs.createReadStream(filePath, { encoding: "utf-8" });
stream.on("error", (err) => this.emit("error", err));
const rl = readline.createInterface({ input: stream });
rl.on("line", (line) => {
lineCount++;
if (line.includes(this.keyword)) {
matchCount++;
}
if (lineCount % 1000 === 0) {
this.emit("progress", { lineCount, matchCount });
}
});
rl.on("close", () => {
this.emit("done", { lineCount, matchCount });
});
}
}
const analyzer = new LogAnalyzer("ERROR");
analyzer.on("progress", ({ lineCount, matchCount }) => {
console.log(`進捗: ${lineCount}行処理 / ${matchCount}件ヒット`);
});
analyzer.on("done", ({ lineCount, matchCount }) => {
console.log(`完了: 全${lineCount}行中、${matchCount}件が"ERROR"を含んでいました`);
});
analyzer.on("error", (err) => {
console.error("ログ解析中にエラーが発生しました:", err.message);
});
analyzer.analyze("app.log");