JavaScriptから法人番号APIを使う方法とCORSの注意点

JavaScriptから国税庁の法人番号システムWeb-APIを使うときは、ブラウザから直接呼ばず、サーバー側(Node.jsやNext.jsのAPIルート)から呼んで、結果だけをブラウザに返すのが正解です。

ブラウザから直接呼ぶと、CORSのエラーで失敗しやすいうえに、利用規約で禁止されているアプリケーションIDの開示になってしまいます。この記事では、その理由と実装例を説明します。法人番号を扱えるAPIの比較は法人番号APIの比較にまとめています。

ブラウザから直接呼んではいけない2つの理由

1. アプリケーションIDが利用者に見えてしまう

法人番号APIは、URLの id パラメータにアプリケーションIDを付けて呼びます。ブラウザで動くJavaScriptから呼ぶと、開発者ツールを開けば誰でもIDを見られます。

Web-API機能利用規約は、アプリケーションIDを第三者に開示することを禁止しています。IDが漏れると、他人に使われて利用を止められるおそれもあります。

2. CORSのエラーになる

ブラウザは、別のドメインのAPIを呼ぶときに、相手側がCORSのヘッダーで許可しているかを確かめます。国税庁のAPIはブラウザから呼ばれる前提で作られていないため、自分のサイトから fetch すると、次のようなエラーで止まります。

Access to fetch at 'https://api.houjin-bangou.nta.go.jp/...' from origin 'https://example.com'
has been blocked by CORS policy

CORSのエラーを回避するプロキシを挟んでブラウザから呼ぶ方法もありますが、結局アプリケーションIDの問題が残ります。サーバー側で呼ぶのが一番安全です。

Node.jsで呼ぶ

Node.js 18以降は fetch が標準で使えます。XMLの解析には fast-xml-parser を使います。

npm install fast-xml-parser
import { XMLParser } from "fast-xml-parser";

const parser = new XMLParser();

export async function fetchCorporations(numbers) {
  const url = new URL("https://api.houjin-bangou.nta.go.jp/4/num");
  url.searchParams.set("id", process.env.NTA_APP_ID);
  url.searchParams.set("number", numbers.join(","));
  url.searchParams.set("type", "12");

  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  const xml = await res.text();
  const corps = parser.parse(xml).corporations?.corporation ?? [];
  // 1件だけのときは配列にならないのでそろえる
  return (Array.isArray(corps) ? corps : [corps]).map((c) => ({
    corporateNumber: String(c.corporateNumber),
    name: c.name,
    address: `${c.prefectureName}${c.cityName}${c.streetNumber}`,
  }));
}

fast-xml-parser は、法人番号のような数字だけの値を数値に変換します。13桁でも桁が落ちることはありませんが、先頭が0の値や郵便番号を扱うなら、new XMLParser({ parseTagValue: false }) にして文字列のまま受け取るほうが安全です。

Next.jsのAPIルートで包む

ブラウザからは自分のサーバーのAPIを呼び、サーバーが国税庁のAPIを呼ぶ形にします。Next.jsのApp Routerなら次のようになります。

// app/api/corporations/[number]/route.js
import { fetchCorporations } from "@/lib/nta";

export async function GET(_request, { params }) {
  const { number } = await params;
  if (!/^\d{13}$/.test(number)) {
    return Response.json({ error: "法人番号は13桁の数字です" }, { status: 400 });
  }
  const [corp] = await fetchCorporations([number]);
  return corp
    ? Response.json(corp)
    : Response.json({ error: "見つかりません" }, { status: 404 });
}

ブラウザ側は、自分のドメインの /api/corporations/1180301018771 を呼ぶだけです。CORSの問題も、IDの開示の問題も起きません。

入力された法人番号は、13桁の数字かどうかに加えて、チェックデジットでも確かめておくと、無駄なアクセスを減らせます(法人番号のチェックデジットの計算方法)。

気をつけること

  • 呼びすぎを防ぐ: フォームの入力のたびに呼ぶと、短時間の大量アクセスになります。13桁がそろってから呼ぶ、結果をキャッシュする、などの工夫をしてください。
  • 10件ずつ: 1回に指定できる法人番号は10件までです。
  • 出典の表示: 取得した情報を表示するサービスを公開するなら、国税庁のWeb-APIを使っている旨と、国税庁が内容を保証しない旨を表示します。

JSONで扱いたい場合

国税庁のAPIの応答はCSVかXMLで、JSONは選べません。JavaScriptで扱いやすいJSONで受け取りたい場合の方法は、法人番号APIをJSONで使う方法で解説しています。

まとめ

JavaScriptから法人番号APIを使うときは、サーバー側で呼んで結果だけをブラウザに返します。CORSのエラーを避けるためだけでなく、アプリケーションIDを守るためでもあります。

ブラウザから直接呼べる形の商用APIもあります(APIキーはサーバー側で保管する前提です)。選択肢は法人番号APIの比較を見てください。

この記事を書いた人

Bango API の開発者です。国税庁の法人番号Web-APIとインボイスWeb-APIを使ったシステムを実装してきた経験をもとに書いています。

本サービスは、国税庁法人番号システムWeb-API機能および国税庁適格請求書発行事業者公表システムのWeb-API機能を利用して取得した情報をもとに作成していますが、サービスの内容は国税庁によって保証されたものではありません。取得した情報は編集・加工して提供しています。