JSON Schemaで型を保証する基本 - API契約と設定ファイルを壊さない
最終更新日: 2026年4月19日
「フロントが期待するレスポンス形式とサーバが返すJSONがズレていて、undefined参照で画面が真っ白になった」 「設定ファイルのキー名をtypoしたまま本番デプロイした」。 こうした事故の大半はJSON Schemaによる型チェックで防げます。 この記事ではJSON Schemaの基本文法と、実際にAPIや設定ファイルを守るための実装パターンをまとめます。
Most "undefined is not a function" bugs and broken config files come from mismatched shapes between what the producer sends and what the consumer expects.JSON Schema codifies that contract. This guide coverstype, required, enum, additionalProperties, validating with Ajv, and wiring schema checks into CI so a malformed payload never ships to production.
図1: JSON Schemaはデータとコードの間の「契約書」
スキーマで弾けば、不正データがビジネスロジックまで到達しない
最小のスキーマ
ユーザー登録APIのリクエストを想定します。email と age が必須、age は0〜150の整数という契約です。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["email", "age"],
"properties": {
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 150 }
},
"additionalProperties": false
}ポイントは additionalProperties: false です。 これを付けないと未知のキーが紛れても通ってしまい、typoに気付けません。 逆に拡張を許したい場合は明示的に true を指定します。
よく使うキーワード早見表
| キーワード | 用途 | 例 |
|---|---|---|
type | 基本型の指定 | string, integer, boolean, array, object, null |
required | 必須プロパティの配列 | ["id", "name"] |
enum | 許可する値を列挙 | ["draft", "published"] |
const | 単一の固定値 | "v2" |
pattern | 正規表現での検証 | "^[A-Z]{3}$" |
minimum / maximum | 数値の範囲 | 価格なら 0 以上 |
minLength / maxLength | 文字列の長さ | パスワード長の下限など |
format | 意味的な型(検証は実装依存) | email, uri, date-time, uuid |
additionalProperties | 未知キーを許すか | 基本 false 推奨 |
Node.jsでAjvを使って検証する
実務で最も使われるバリデータは Ajv。 Fastify や tRPC のバリデータ層の中身もAjvです。
import Ajv from "ajv";
import addFormats from "ajv-formats";
const ajv = new Ajv({ allErrors: true, strict: true });
addFormats(ajv); // format: email / uri などを有効化
const schema = {
type: "object",
required: ["email", "age"],
properties: {
email: { type: "string", format: "email" },
age: { type: "integer", minimum: 0, maximum: 150 },
},
additionalProperties: false,
};
const validate = ajv.compile(schema);
const data = { email: "test@example.com", age: 30 };
if (!validate(data)) {
console.error(validate.errors);
// [{ instancePath: "/age", message: "must be <= 150", ... }]
}allErrors: true で「全エラーをまとめて返す」動作になります。 デフォルトだと最初のエラーで打ち切るため、フォーム検証用途では基本有効化しておくのが無難です。
図2: バリデーション層の配置
TypeScriptの型と同期する
同じ情報を型とスキーマに二重メンテするのは地獄です。現実的な選択肢は2つ。
- スキーマから型を生成する:
json-schema-to-typescriptなどでビルド時に.d.tsを出力。 スキーマが真実の源(source of truth)になる。 - Zod / Valibot など「型とスキーマ一体型」のライブラリを使う: TypeScriptの型とバリデータが同じ宣言から導かれるため、手書きJSON Schemaより保守が楽。 必要なら
zod-to-json-schemaで外部公開用JSON Schemaを書き出せる。
既にOpenAPIを運用しているなら、その中にJSON Schemaがそのまま含まれているため、 OpenAPI定義をsource of truthにしてクライアント型・バリデータを生成する方が一貫します。
設定ファイル(tsconfig.json / package.jsonなど)を守る
VS Codeは $schema フィールドを尊重するため、自作の設定ファイルでも スキーマを設置すれば補完・型チェックが効きます。
// app.config.json
{
"$schema": "./app.config.schema.json",
"port": 3000,
"logLevel": "info"
}CIでは ajv validate -s schema.json -d config.json をpre-commitまたはPRチェックに組み込むと、 設定ファイルのtypoがmainにマージされるのを防げます。
よくある落とし穴
type: "number"は小数を許す。 整数が欲しいならinteger。numberだと1.5も通る。requiredは配列、propertiesはオブジェクト。 これを逆に書くエラーが非常に多い。format: "date"はISO 8601のYYYY-MM-DD。 日本式の2026/04/19は通らない。nullを許したいならtype: ["string", "null"]。nullable: trueはJSON Schemaではなく OpenAPI 3.0 の拡張。- 巨大スキーマの参照は
$refで分割。 共通のUser型を1箇所に書き、各エンドポイントで$refする。
実装で使うツール
スキーマを書く前に、対象のサンプルJSONを整形して構造を把握するのが効率的です。JSON Formatter で整形後、 ネストの深さや繰り返し構造を見てからスキーマを設計すると抜けが減ります。 サンプルJSONから型を素早く作りたい場合はJSON to TypeScript で下書きを作り、 それをZodやJSON Schemaに書き換える方が手早いです。
まとめ
JSON Schemaは「データとコードの間の契約書」です。 APIリクエスト・レスポンス・設定ファイルの境界にバリデーション層を1枚置くだけで、 本番障害の相当数を未然に防げます。 ZodやOpenAPIを source of truth にしてスキーマを生成する運用に乗せれば、 型の二重管理コストも避けられます。
English summary
JSON Schema defines the shape your API or config file must follow. Start withtype, required, and properties; useenum for closed sets and additionalProperties: false to reject typos. Validate at the boundary with Ajv and run the schema check in CI. The real leverage is catching contract violations early — before an undefined crashes the UI or a typoed config reaches production.