この単位では、Node.js 側の JavaScript、JSON 処理、実務寄りの小さな設計パターン、JavaScript の落とし穴をまとめて扱う。
Unit 01〜11 で扱った内容を、Node.js 実行、ファイル操作、API レスポンス風データ、データ変換、関数分割、注意点の整理としてつなげる。
最後の総合整理に近い位置づけとして、個別の構文だけではなく、実務で読みやすく保つための小さな関数設計も確認する。
一方で、大きなアーキテクチャやフレームワーク設計までは扱わず、JavaScript の基礎から TypeScript / React へ進む前の土台を整理する。
この単位で扱う内容は次の通り。
process- 環境変数
- コマンドライン引数
node:fs/promisesnode:path- ファイル読み書き
- JSON ファイル読み込み
- Node.js での
fetch - npm scripts
- devDependencies の概要
- JSON とは何か
- JavaScript object との違い
JSON.stringifyJSON.parse- API レスポンス風データ
- ネストしたデータの取り出し
- null 混じりデータ
- optional chaining
- default value
- 表示用データへの変換
- 集計
- group by
- sort
- filter
- ページング風処理
- query parameter 風 object 作成
- mapper 関数
- predicate 関数
- validator 関数
- formatter 関数
- normalizer 関数
- config object
- options object
- early return
- guard clause
- lookup object
- strategy object 的な分岐
- 関数分割
- 副作用を端に寄せる
- データ変換パイプライン
- UI 表示用データへの変換
==の罠- truthy / falsy
null/undefinedNaN- 浮動小数点誤差
- shallow copy
sortの破壊性sortの比較関数forEachと async- object の参照共有
thisvar- hoisting
- Date
- timezone
- optional chaining の使いすぎ
- default 値の扱い
- Promise のエラー漏れ
- import / export の混乱
この単位のファイル構成は次の通り。
src/
12-node-json-practical-patterns/
index.js
runtime-context.js
path-and-url-basics.js
file-read-write.js
json-file-loading.js
node-fetch-basics.js
json-stringify-parse.js
api-response-shape.js
nested-nullish-data.js
optional-default-values.js
display-data-transform.js
group-sort-filter-pagination.js
query-params-object.js
mapper-predicate-validator.js
formatter-normalizer.js
config-options-object.js
early-return-guard-clause.js
strategy-lookup-object.js
function-pipeline.js
side-effect-boundary.js
pitfalls-equality-nullish.js
pitfalls-number-copy-sort.js
pitfalls-async-this-var-date-import.js
fixtures/
app-config.json
tasks.json
users-response.json
docs/
12-node-json-practical-patterns.md
各ファイルの役割は次の通り。
index.js- Unit 12 のサンプルを順番に実行する入口。
runtime-context.jsprocess、環境変数、コマンドライン引数、npm scripts の入口を確認する。
path-and-url-basics.jsnode:path、import.meta.url、file URL と path の変換を確認する。
file-read-write.jsnode:fs/promisesによるファイル読み書きを確認する。
json-file-loading.js- JSON ファイルを読み込み、設定値や固定データとして扱う流れを確認する。
node-fetch-basics.js- Node.js での
fetch、response handling、JSON 取得を確認する。
- Node.js での
json-stringify-parse.js- JSON と JavaScript object の違い、
JSON.stringify、JSON.parseを確認する。
- JSON と JavaScript object の違い、
api-response-shape.js- API レスポンス風データから
status、meta、dataを取り出す。
- API レスポンス風データから
nested-nullish-data.js- ネストしたデータ、null 混じりデータ、optional chaining を確認する。
optional-default-values.js- default value、
??、optional chaining の扱いを確認する。
- default value、
display-data-transform.js- API / JSON の生データを表示用データへ変換する。
group-sort-filter-pagination.jsfilter、sort、group by、ページング風処理を確認する。
query-params-object.js- query parameter 風 object と query string を作る。
mapper-predicate-validator.js- mapper 関数、predicate 関数、validator 関数を確認する。
formatter-normalizer.js- formatter 関数、normalizer 関数を確認する。
config-options-object.js- config object、options object、nested object の merge を確認する。
early-return-guard-clause.js- early return と guard clause を確認する。
strategy-lookup-object.js- lookup object、strategy object 的な分岐を確認する。
function-pipeline.js- 関数分割とデータ変換パイプラインを確認する。
side-effect-boundary.js- ファイル読み書きの副作用を端に寄せる構成を確認する。
pitfalls-equality-nullish.js==、truthy / falsy、NaN、default 値の扱いを確認する。
pitfalls-number-copy-sort.js- 浮動小数点誤差、shallow copy、
sortの破壊性と比較関数を確認する。
- 浮動小数点誤差、shallow copy、
pitfalls-async-this-var-date-import.jsforEachと async、this、var、Date / timezone、Promise のエラー、import / export 方針を確認する。
fixtures/app-config.json- 設定ファイルとして読み込む JSON。
fixtures/tasks.json- 一覧処理、表示用変換、集計に使う task データ。
fixtures/users-response.json- API レスポンス風の user データ。
12-node-json-practical-patterns.md- この単位の内容、実行方法、注目ポイント、確認観点をまとめたドキュメント。
Node.js 側のサンプルは、リポジトリ直下で次のコマンドを実行する。
npm run unit:12ESLint を確認する場合は次を実行する。
npm run lintPrettier の整形チェックを行う場合は次を実行する。
npm run format:check整形が必要な場合は次を実行する。
npm run formatコードは次の順番で読むと理解しやすい。
src/12-node-json-practical-patterns/index.jssrc/12-node-json-practical-patterns/runtime-context.jssrc/12-node-json-practical-patterns/path-and-url-basics.jssrc/12-node-json-practical-patterns/file-read-write.jssrc/12-node-json-practical-patterns/json-file-loading.jssrc/12-node-json-practical-patterns/node-fetch-basics.jssrc/12-node-json-practical-patterns/json-stringify-parse.jssrc/12-node-json-practical-patterns/api-response-shape.jssrc/12-node-json-practical-patterns/nested-nullish-data.jssrc/12-node-json-practical-patterns/optional-default-values.jssrc/12-node-json-practical-patterns/display-data-transform.jssrc/12-node-json-practical-patterns/group-sort-filter-pagination.jssrc/12-node-json-practical-patterns/query-params-object.jssrc/12-node-json-practical-patterns/mapper-predicate-validator.jssrc/12-node-json-practical-patterns/formatter-normalizer.jssrc/12-node-json-practical-patterns/config-options-object.jssrc/12-node-json-practical-patterns/early-return-guard-clause.jssrc/12-node-json-practical-patterns/strategy-lookup-object.jssrc/12-node-json-practical-patterns/function-pipeline.jssrc/12-node-json-practical-patterns/side-effect-boundary.jssrc/12-node-json-practical-patterns/pitfalls-equality-nullish.jssrc/12-node-json-practical-patterns/pitfalls-number-copy-sort.jssrc/12-node-json-practical-patterns/pitfalls-async-this-var-date-import.js
前半で Node.js API と JSON 処理を確認し、後半で実務寄りの小さな関数設計と落とし穴を整理する。
最後の Unit のため、これまでの Unit で扱った値、比較、配列、object、非同期、this、Date、import / export を振り返る構成にしている。
runtime-context.js では、Node.js の実行環境に関する情報を process から取得している。
const runtimeInfo = {
nodeVersion: process.version,
platform: process.platform,
currentWorkingDirectory: process.cwd(),
};process は Node.js 側の代表的な global object。
ブラウザの window とは別物であり、環境変数、コマンドライン引数、実行ディレクトリなどを扱う入口になる。
path-and-url-basics.js では、現在ファイルの URL を path に変換している。
const currentFilePath = fileURLToPath(import.meta.url);
const currentDirectoryPath = path.dirname(currentFilePath);
export function resolveUnitPath(...paths) {
return path.join(currentDirectoryPath, ...paths);
}Node.js の ES Modules では、CommonJS の __dirname をそのまま使えない。
import.meta.url、fileURLToPath、path.dirname を組み合わせることで、現在ファイルからの相対 path を安全に組み立てられる。
json-file-loading.js では、JSON ファイルを読み込む処理を小さな関数にしている。
async function readJsonFile(filePath) {
const text = await fs.readFile(filePath, 'utf8');
return JSON.parse(text);
}ファイルから読み込んだ時点では文字列。
JavaScript object として扱うには JSON.parse が必要になる。
外部ファイルや API response は自分のコード外から来る値のため、本来は parse 後の形も検証対象になる。
api-response-shape.js では、API レスポンス風データから必要な値を取り出している。
const responseStatus = apiResponse.status;
const requestId = apiResponse.meta.requestId;
const tasks = apiResponse.data;API response は配列だけではなく、通信結果、ページ情報、request id などを一緒に持つことが多い。
先に response 全体の形を把握してから、画面表示や処理に必要な data を取り出すと読みやすい。
display-data-transform.js では、生の task data を表示用 item に変換している。
function createTaskDisplayItem(task) {
const statusLabelMap = {
todo: '未着手',
doing: '進行中',
done: '完了',
};
return {
id: task.id,
title: task.title,
statusLabel: statusLabelMap[task.status] ?? '不明',
assigneeName: task.assignee?.name ?? '未担当',
dueDateLabel: task.dueDate ?? '期限なし',
};
}API や JSON のデータを、そのまま UI に出すとは限らない。
表示用 label、fallback、日付表示などを mapper 関数にまとめると、UI 側の責務を軽くできる。
mapper-predicate-validator.js では、mapper、predicate、validator を分けている。
function mapTaskToSummary(task) {
return {
id: task.id,
label: `${task.id}: ${task.title}`,
active: task.status !== 'done',
};
}
function isActiveTask(task) {
return task.status !== 'done';
}
function validateTaskInput(input) {
const errors = [];関数名が役割を表していると、処理を読む前に意図を推測できる。
特に map、filter、validation、format、normalize のような処理は、実務で何度も出てくる。
side-effect-boundary.js では、ファイル読み込み、データ変換、ファイル書き込みを分けている。
function createSummary(tasks) {
return {
total: tasks.length,
completed: tasks.filter((task) => task.status === 'done').length,
active: tasks.filter((task) => task.status !== 'done').length,
};
}createSummary は入力を受け取り、結果を返すだけの関数。
ファイル読み書きは別関数に寄せているため、中心のデータ変換を単独で理解しやすい。
json-stringify-parse.js では、JavaScript object を JSON 文字列へ変換している。
const compactJsonText = JSON.stringify(user);
const prettyJsonText = JSON.stringify(user, null, 2);
const parsedUser = JSON.parse(compactJsonText);JSON は文字列表現。
JavaScript object そのものではない。
ファイル保存や API 通信では JSON 文字列として扱い、コード内で object として処理するときに parse する。
また、JSON として表現できない値もある。
const valueWithUnsupportedTypes = {
id: 1,
name: undefined,
callback: () => 'ignored',
active: true,
};
const unsupportedTypesJsonText = JSON.stringify(valueWithUnsupportedTypes);undefined や function は通常のデータとして JSON に残らない。
保存や通信に使う値は、JSON として表現できる形に揃える必要がある。
optional-default-values.js では、optional chaining と default value を使っている。
const firstTitle = response.data?.items?.[0]?.title ?? '未設定';optional chaining は null 混じりデータを読むときに便利。
ただし、本来必ず存在すべき値まで ?. で読み飛ばすと、データ不備に気づくのが遅くなる。
必須値は guard clause や validator で確認し、任意値だけ optional chaining で扱う方が意図が明確になる。
pitfalls-equality-nullish.js では、|| と ?? の違いを確認している。
const defaultValueResults = {
zeroByOr: 0 || 10,
zeroByNullish: 0 ?? 10,
emptyStringByOr: '' || 'default',
emptyStringByNullish: '' ?? 'default',
};|| は falsy 全般を default に置き換える。
?? は null / undefined のときだけ default を使う。
0 や空文字を有効な値として扱うなら、?? の方が意図に合う場合が多い。
group-sort-filter-pagination.js では、sort の前に shallow copy を作っている。
const sortedActiveTasks = [...activeTasks].sort((taskA, taskB) => {
return taskA.priority - taskB.priority;
});sort は破壊的 method。
元 array を変えたくない場合は、[...array] で copy してから並べ替える。
React の state や再利用する配列では特に重要になる。
pitfalls-async-this-var-date-import.js では、forEach に async callback を渡している。
[1, 2, 3].forEach(async (value) => {
const result = await waitAndReturn(value * 2);
results.push(result);
});
return results;forEach は callback の Promise を待たない。
逐次実行したい場合は for...of と await、並列実行したい場合は map と Promise.all を検討する。
pitfalls-async-this-var-date-import.js では、UTC 表現と Tokyo 表示を分けている。
const date = new Date('2026-05-31T00:00:00.000Z');
const isoText = date.toISOString();
const tokyoText = date.toLocaleString('ja-JP', { timeZone: 'Asia/Tokyo' });toISOString は UTC 表現。
画面表示では locale や timezone を指定することがある。
日付だけを扱っているつもりでも、timezone によって表示日がずれることがある。
runtime-context.js では、process.argv からアプリ側の引数を取り出している。
const userArgs = process.argv.slice(2);
const limitText = readOptionValue(userArgs, '--limit', '3');
const parsedLimit = Number.parseInt(limitText, 10);
const limit = Number.isNaN(parsedLimit) ? 3 : parsedLimit;Node.js の小さな CLI、build script、変換 script では、コマンドライン引数を読む場面がある。
引数は文字列で渡るため、数値として使うなら parse と fallback を明示する。
file-read-write.js では、JSON の書き込みと読み戻しを 1 つの関数に閉じ込めている。
await fs.writeFile(outputFilePath, jsonText, 'utf8');
const readText = await fs.readFile(outputFilePath, 'utf8');
const readSummary = JSON.parse(readText);ファイル操作は副作用。
実務では、データ変換処理とファイル操作を分けると、テストしやすく、変更にも強くなる。
query-params-object.js では、検索条件 object から query string を作っている。
const queryObject = buildQueryObject({
page: 2,
perPage: 20,
keyword: 'node',
status: 'doing',
sort: undefined,
});
const queryString = buildQueryString(queryObject);画面の検索条件、API の一覧取得、URL の query parameter では、条件 object から文字列を作ることが多い。
先に object として整理し、不要な値を除外してから URLSearchParams に渡すと扱いやすい。
strategy-lookup-object.js では、status に対応する label を object にまとめている。
const statusLabelLookup = {
todo: '未着手',
doing: '進行中',
done: '完了',
};
function getStatusLabel(status) {
return statusLabelLookup[status] ?? '不明';
}条件分岐が単純な対応表なら、lookup object にすると読みやすい。
UI 表示 label、権限名、ステータス名、CSS class 名などでよく使う。
strategy-lookup-object.js では、sort 処理を object に登録している。
const sortStrategies = {
priorityAsc: (taskA, taskB) => taskA.priority - taskB.priority,
titleAsc: (taskA, taskB) => taskA.title.localeCompare(taskB.title, 'ja-JP'),
};
function sortTasks(tasks, strategyName) {
const compare = sortStrategies[strategyName] ?? sortStrategies.priorityAsc;
return [...tasks].sort(compare);
}if / switch が増えすぎる前に、分岐ごとの処理を関数として分けられる。
一覧の sort、format、権限別処理、出力形式の切り替えなどに使いやすい。
function-pipeline.js では、normalize、filter、sort、map を順番に適用している。
return tasks
.map((task) => normalizeTask(task))
.filter((task) => isVisibleTask(task))
.sort((taskA, taskB) => sortByPriority(taskA, taskB))
.map((task) => mapToDisplayItem(task));React に進むと、API response をそのまま JSX に出すのではなく、表示用の props や view model に変換することが多い。
小さい関数を組み合わせると、どの段階で何をしているかが見えやすい。
json-file-loading.js では、JSON を parse してそのまま使っている。
const config = await readJsonFile(configPath);
const tasks = await readJsonFile(tasksPath);JavaScript では parse 後の値がどの形かを実行時に確認する必要がある。
TypeScript に進むと、Task 型や AppConfig 型を定義して、読み込んだ後の値をどう扱うかを型でも表現したくなる。
ただし、TypeScript の型は実行時の JSON を自動検証しない。
外部入力に対しては、validator 関数や schema validation の考え方が必要になる。
mapper-predicate-validator.js では、役割ごとに関数を分けている。
function mapTaskToSummary(task) {
return {
id: task.id,
label: `${task.id}: ${task.title}`,
active: task.status !== 'done',
};
}
function isActiveTask(task) {
return task.status !== 'done';
}TypeScript では、mapper の入力型と出力型、predicate の条件、validator の result 型を明示できる。
関数の役割が分かれているほど、型の境界も分けやすくなる。
display-data-transform.js では、task を表示用 item に変換している。
return {
id: task.id,
title: task.title,
statusLabel: statusLabelMap[task.status] ?? '不明',
assigneeName: task.assignee?.name ?? '未担当',
dueDateLabel: task.dueDate ?? '期限なし',
};React では、JSX の中に status label、fallback、日付表示、null 判定を直接書きすぎると読みにくくなる。
表示用データを作ってから component に渡すと、JSX は表示に集中しやすい。
pitfalls-number-copy-sort.js では、spread による copy が shallow copy であることを確認している。
const copiedTask = { ...originalTask };
copiedTask.assignee.name = 'Updated Alice';また、sort が破壊的であることも確認している。
const mutableNumbers = [3, 1, 2];
const sortedSameReference = mutableNumbers.sort((numberA, numberB) => numberA - numberB);React の state では、元 object や元 array を直接変更しないことが重要になる。
shallow copy の限界と破壊的 method を理解しておくと、意図しない再描画漏れや状態破壊を避けやすい。
pitfalls-async-this-var-date-import.js では、forEach と async の注意点を扱っている。
[1, 2, 3].forEach(async (value) => {
const result = await waitAndReturn(value * 2);
results.push(result);
});React の event handler や useEffect 内で API 通信を扱う場合も、Promise の待ち方や error handling が重要になる。
逐次実行、並列実行、エラーをどこで catch するかを JavaScript の段階で理解しておく必要がある。
config-options-object.js では、任意設定を options object として受け取っている。
function createListOptions(options = {}) {
const { page = 1, pageSize = 10, sort = 'createdAt' } = options;
return {
page,
pageSize,
sort,
};
}React の component props や custom hook の引数でも、複数の設定値を object で渡す場面が多い。
default value、任意項目、設定の merge を JavaScript で理解しておくと、TypeScript の optional property や default props 的な設計にもつながる。
この単位を読み終えたら、次を確認する。
processから Node.js の実行環境を取得できるか- 環境変数とコマンドライン引数の違いを説明できるか
- npm scripts と devDependencies の関係を概要レベルで説明できるか
node:pathを使って path を組み立てる理由を説明できるか- ES Modules で
import.meta.urlを使う理由を説明できるか node:fs/promisesでファイル読み書きを行う流れを説明できるか- JSON と JavaScript object の違いを説明できるか
JSON.stringifyとJSON.parseの役割を説明できるか- API response の
status/meta/dataを分けて読めるか - null 混じりデータで optional chaining を使う場面を説明できるか
??と||の default 値の違いを説明できるか- API / JSON の生データを表示用データへ変換できるか
filter、sort、group by、ページング風処理を説明できるか- query parameter 風 object から query string を作る流れを説明できるか
- mapper / predicate / validator / formatter / normalizer の役割を説明できるか
- config object と options object の違いを説明できるか
- early return と guard clause の利点を説明できるか
- lookup object と strategy object 的な分岐を説明できるか
- 関数分割とデータ変換パイプラインの利点を説明できるか
- 副作用を端に寄せる理由を説明できるか
==、truthy / falsy、NaNの落とし穴を説明できるか- shallow copy と object の参照共有を説明できるか
sortの破壊性と比較関数の必要性を説明できるかforEachと async の注意点を説明できるかthis、var、hoisting の注意点を概要レベルで説明できるか- Date と timezone の注意点を説明できるか
- Promise の error handling が漏れる問題を説明できるか
- import / export の混乱を避けるための方針を説明できるか