本文へスキップ

e-Gov法令API v2をTypeScriptで使う:依存なしfetchで法令検索からJSON保存まで

e-Gov法令API v2をTypeScript(Node.js標準のfetch、依存パッケージなし)で呼び出し、法令名検索→法令ID確定→本文取得→JSON保存まで動く116行のサンプルを解説。Node.js 24.8・22.14・tsxの3通りで2026年9月14日に実行確認しました。

codeagent.jp編集部 更新 約5分

e-Gov法令API v2をTypeScriptから使うのに、HTTPクライアントもビルドツールも要りません。Node.js標準の fetchURL.searchParams だけで、法令名検索→法令ID確定→本文取得→JSON保存まで116行で動きます。 依存がゼロなので、MCPサーバーや定期実行スクリプトの土台にそのまま流用できます。

この記事のサンプルは、Node.js 24.8.0(.ts をそのまま実行)、Node.js 22.14.0(--experimental-strip-types)、npx tsx の3通りで2026年9月14日に実行し、同じ結果になることを確認しています。Pythonで同じことをする手順はPython実装の記事に、使っているエンドポイントの一覧は逆引きリファレンスにあります。

  1. lawslaw_data の2段階。 法令名から候補を取り、完全一致が1件のときだけ本文を取ります。
  2. 日本語の文字コードは URL.searchParams に任せる。 curlで起きるShift_JIS混入が構造的に起きません。
  3. 保存JSONに出典情報を同梱する。 取得日時、URL、asof、履歴IDを本文と同じファイルに入れます。
  4. 終了コードで結果を伝える。 0=成功、1=APIエラーや複数一致、2=引数不足。定期実行の分岐に使えます。

サンプルコード(116行)

egov-fetch.ts として保存してください。Node.js 24なら node egov-fetch.ts "労働基準法" で動きます。

// e-Gov 法令API v2: 法令名検索 → 法令ID確定 → 本文取得 → JSON保存
// 実行例:
// node --experimental-strip-types egov-fetch.ts "労働基準法" (Node.js 22.6 以降)
// node egov-fetch.ts "労働基準法" 2025-06-01 (Node.js 24 以降は型ストリップが既定)
import { mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
const BASE = 'https://laws.e-gov.go.jp/api/2';
type LawInfo = {
law_id: string;
law_num: string;
law_type: string;
promulgation_date: string;
};
type RevisionInfo = {
law_revision_id: string;
law_title: string;
abbrev: string | null;
amendment_enforcement_date: string;
current_revision_status: 'CurrentEnforced' | 'UnEnforced' | 'PreviousEnforced' | 'Repeal';
};
type LawsResponse = {
total_count: number;
count: number;
next_offset: number | null;
laws: { law_info: LawInfo; revision_info: RevisionInfo }[];
};
type LawDataResponse = {
law_info: LawInfo;
revision_info: RevisionInfo;
law_full_text: unknown;
};
type ErrorInfo = { code: string; message: string };
async function egovGet<T>(endpoint: string, params: Record<string, string> = {}): Promise<T> {
const url = new URL(`${BASE}/${endpoint}`);
for (const [key, value] of Object.entries(params)) url.searchParams.set(key, value);
const res = await fetch(url, {
headers: { Accept: 'application/json' },
signal: AbortSignal.timeout(30_000),
});
if (!res.ok) {
const body = await res.text();
let detail = body.slice(0, 200);
try {
const err = JSON.parse(body) as ErrorInfo;
detail = `${err.code} ${err.message}`;
} catch {
// JSON 以外(HTML の 400 など)はそのまま
}
throw new Error(`HTTP ${res.status} ${url.pathname}: ${detail}`);
}
return (await res.json()) as T;
}
async function resolveLawId(title: string): Promise<LawInfo & RevisionInfo> {
const data = await egovGet<LawsResponse>('laws', { law_title: title, law_type: 'Act', limit: '10' });
if (data.count === 0) throw new Error(`「${title}」に一致する法律がありません`);
const exact = data.laws.filter((l) => l.revision_info.law_title === title);
if (exact.length === 1) return { ...exact[0].law_info, ...exact[0].revision_info };
const lines = data.laws.map((l) => ` ${l.law_info.law_id} ${l.revision_info.law_title}`);
throw new Error(`完全一致が1件に絞れません(${data.total_count}件)。ID を指定してください:\n${lines.join('\n')}`);
}
async function main(): Promise<void> {
const [title, asof] = process.argv.slice(2);
if (!title) {
console.error('usage: node egov-fetch.ts <法令名> [asof=YYYY-MM-DD]');
process.exitCode = 2;
return;
}
const law = await resolveLawId(title);
console.log(`law_id=${law.law_id} ${law.law_title} (${law.law_num})`);
const params: Record<string, string> = { json_format: 'light' };
if (asof) params.asof = asof;
const data = await egovGet<LawDataResponse>(`law_data/${law.law_id}`, params);
const rev = data.revision_info;
console.log(`law_revision_id=${rev.law_revision_id} enforced=${rev.amendment_enforcement_date} status=${rev.current_revision_status}`);
const outDir = path.join(process.cwd(), 'out');
await mkdir(outDir, { recursive: true });
const file = path.join(outDir, `${rev.law_revision_id}.json`);
await writeFile(
file,
JSON.stringify(
{
fetched_at: new Date().toISOString(),
source: `${BASE}/law_data/${law.law_id}${asof ? `?asof=${asof}` : ''}`,
asof: asof ?? null,
law_info: data.law_info,
revision_info: rev,
law_full_text: data.law_full_text,
},
null,
2,
),
'utf8',
);
console.log(`saved: ${file}`);
}
main().catch((err: unknown) => {
console.error(err instanceof Error ? err.message : String(err));
process.exitCode = 1;
});

型は type エイリアスと as キャストだけに絞ってあります。enumnamespace、コンストラクタの引数プロパティといった「型を消すだけでは済まない構文」を使っていないので、Node.jsの型ストリップで動きます。

実行結果

Node.js 24.8.0で、現行版と2025年6月1日時点を続けて取りました。

$ node egov-fetch.ts "労働基準法"
law_id=322AC0000000049 労働基準法 (昭和二十二年法律第四十九号)
law_revision_id=322AC0000000049_20260717_508AC0000000060 enforced=2026-07-17 status=CurrentEnforced
saved: ...\out\322AC0000000049_20260717_508AC0000000060.json
$ node egov-fetch.ts "労働基準法" 2025-06-01
law_id=322AC0000000049 労働基準法 (昭和二十二年法律第四十九号)
law_revision_id=322AC0000000049_20250601_504AC0000000068 enforced=2025-06-01 status=PreviousEnforced
saved: ...\out\322AC0000000049_20250601_504AC0000000068.json

asof の有無で履歴IDが分かれ、ファイル名にもそのまま残ります。保存したJSONは fetched_at source asof law_info revision_info law_full_text の6キーで、source には https://laws.e-gov.go.jp/api/2/law_data/322AC0000000049?asof=2025-06-01 が入っていました。後から「どの時点の条文か」を確認できる形です。

116行
サンプルの長さ
型定義を含む。依存パッケージ 0
3通り
実行できた環境
Node 24.8 / Node 22.14 + フラグ / npx tsx
442 KB
保存した労働基準法(light)
2025-06-01 時点。民法は 2.0 MB
0 / 1 / 2
終了コード
成功 / API エラー・複数一致 / 引数不足
2026-09-14 に Windows 11 で実行。ファイルサイズは JSON.stringify(…, null, 2) で整形後の値

実行環境の違い

同じファイルを3通りで動かしました。

Node.js 24.8.0(そのまま実行)
Node.js 22.14.0 / npx tsx
コマンド
node egov-fetch.ts 労働基準法
node --experimental-strip-types egov-fetch.ts 労働基準法 / npx tsx egov-fetch.ts 労働基準法
追加の警告
なし
22.14 は ExperimentalWarning: Type Stripping is an experimental feature が stderr に出る。tsx はなし
フラグを忘れると
(不要)
22.14 は ERR_UNKNOWN_FILE_EXTENSION: Unknown file extension ".ts" で停止
出力・保存結果
同じ
同じ(履歴ID・ファイル名・JSON のキー構成が一致)
向いている場面
新規のスクリプト・定期実行
既存の Node 22 環境、または tsx を既に使っている場合
2026-09-14 の実行結果。Node 22 の型ストリップは 22.6 で導入された実験的機能

新しく書くならNode.js 24でフラグなしが一番手数が少なく、既存のNode 22環境ならフラグを付けるだけで済みます。tsx を入れる必要はありませんでした。

設計で決めたこと

法令IDは完全一致でしか確定しない

laws は部分一致なので、「民法」で11件、「会社」で38件ヒットします。サンプルは revision_info.law_title が入力と完全一致する法令が1件のときだけ先へ進み、それ以外は候補を表示して止まります。

$ node egov-fetch.ts "会社"
完全一致が1件に絞れません(38件)。ID を指定してください:
321AC0000000007 会社経理応急措置法
322AC0000000151 昭和二十二年法律第百五十一号(国際電気通信株式会社等の...)
323CO0000000402 会社等臨時措置法等を廃止する政令
...
(終了コード 1)

「民法」は11件ヒットしても完全一致が 129AC0000000089 の1件だったので、そのまま本文取得に進みました。先頭1件を黙って採用する実装にしなかったのは、laws の既定の並びが law_info.law_id 昇順で、目的の法令が先頭に来る保証がないからです。通称で引けない場合の考え方は通称と法令名のズレの記事を参照してください。

文字コードは URL に任せる

curlでは、呼び出す殻によって日本語がShift_JISで送られて0件になる現象がありました(エラーと制限の記事)。URL.searchParams.set はUTF-8でパーセントエンコードするので、この問題が構造的に起きません。角括弧を含む elm=LawTitle[1] のような値も %5B %5D に自動でエンコードされます。

エラー本文を捨てない

res.ok でなければ本文を読み、JSONなら codemessage を、JSON以外(HTMLの400など)なら先頭200文字をそのまま例外に載せます。不正な asof を渡したときの表示です。

$ node egov-fetch.ts "労働基準法" 2025-13-45
law_id=322AC0000000049 労働基準法 (昭和二十二年法律第四十九号)
HTTP 400 /api/2/law_data/322AC0000000049: 400004 日付(asof等)が誤っています。
(終了コード 1)

APIが返したコードと日本語メッセージがそのまま出るので、エラーコードの一覧と突き合わせられます。

process.exit ではなく exitCode

最初は catch の中で process.exit(1) を呼んでいました。すると法令名が見つからない場合に、Node.js 24.8.0(Windows)で Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 76 が出て終了コードが127になりました。fetchの内部ハンドルが閉じ切る前にプロセスを落としたためと見ています。process.exitCode = 1 に変えて自然終了させると解消し、終了コードも1になりました。非同期処理の直後に process.exit を呼ぶのは避けた方が安全です。

MCP や定期実行へ広げるとき

このサンプルは「1法令を1回取る」までですが、egovGet をそのまま使い回せます。

  • MCPサーバーにするresolveLawIdlaw_data の呼び出しをツールに分け、返り値に law_revision_idsource を必ず含めます。XMLを扱う場合の型設計はXML→JSONの記事にあります。
  • 改正を監視する:本文ではなく law_revisions を取り、履歴IDの集合を前回と比べます。実装と運用は改正チェックの定期実行に分けました。
  • 本文を絞るlaw_dataelm: 'MainProvision-Article_32' を足せば1条だけ(1.7KB)になります。全文を毎回保存する必要はありません。

まとめ

  • Node.js標準の fetchURL だけで、法令名検索→ID確定→本文取得→JSON保存が116行、依存ゼロで動く
  • Node.js 24.8はフラグなし、22.14は --experimental-strip-typesnpx tsx の3通りで同じ結果
  • 法令IDは完全一致1件のときだけ確定する。「会社」は38件で止まり、「民法」は1件に絞れた
  • 保存JSONに fetched_at source asof law_revision_id を同梱し、後から時点を検証できる形にする
  • 非同期処理の直後の process.exit はNode 24.8で assertion を出した。process.exitCode を使う

パラメータの組み立てを先に画面で試したい場合はe-Gov法令APIリクエストビルダー、エージェントから使う全体像はe-Gov法令API活用ガイドから辿れます。

関連して読む

この記事の情報・検証メモ
Tags
公開日
情報確認
参考リンク
2件
更新性
定期更新
更新管理

仕様・料金・提供範囲が変わりやすいテーマは、公開日・更新日・情報確認日を分けて管理します。 導入前には必ず記事末尾の一次情報と公式ドキュメントで最新状況を確認してください。

検証メモ
Node.js 24.8.0(型ストリップ既定) Node.js 22.14.0 + --experimental-strip-types npx tsx(Node.js 24.8.0) e-Gov法令API v2 / OpenAPI 2.1.139、実行日 2026-09-14(Windows 11)
図解を保存・共有

記事の要点を1枚にまとめました。画像は新しいタブで開いて保存できます。

e-Gov法令API v2をTypeScriptで使う:依存なしfetchで法令検索からJSON保存まで 依存なしのfetchだけで、法令名→ID→本文→JSON保存が116行で動く。IDは完全一致で確定する 設計:laws で候補を取り、法令名の完全一致が1件のときだけ先へ進む。law_data は json_format=light、asof は任意引数。保存 JSON に取得日時・URL・asof・履歴IDを同梱。 実行環境:Node.js 24 は node egov-fetch.ts でそのまま動く。Node.js 22.14 は --experimental-strip-types が必要。npx tsx でも同じ結果。 実行して分かったこと:URL.searchParams に任せると日本語の文字コード問題が消える。「会社」は38件で止まり、「民法」は1件に確定した。catch 内の process.exit(1) が Node 24.8 で assertion を出した。
e-Gov法令API v2をTypeScriptで使う:依存なしfetchで法令検索からJSON保存まで 記事の要約 2026.09.14 実装・公開事例
Primary sources

一次情報・参考リンク

About the author
codeagent.jp編集部

Claude Code / Codex / MCP を個人開発サイト運用と公開MCPサーバー開発で試し、一次情報・検証ログ・失敗例をもとに整理します。