JSON Schemaの書き方|API検証の5ステップ

目次
はじめに
APIの仕様書に「金額はnumber」「メールアドレスはstring」と書いても、実装時の判断は残る。0円を許すのか。項目を省略できるのか。申請種別によって必須項目が変わるのか。余分な項目を受け取ったら無視するのか。
人が読む表だけでは、フロントエンド、バックエンド、テスト、AIコード生成が別々の解釈をしやすい。
JSON Schemaは、JSONデータの構造と制約を機械可読にする仕様だ。ただし、型を付けるだけでは業務で使える契約にならない。この記事では、経費申請APIを題材に、JSON Schemaの書き方を5ステップで整理する。
JSON Schemaは「入力例」ではなく検証できる契約
JSON Schema Draft 2020-12のValidation仕様には、`type`、`required`、`minimum`、`pattern`など、JSONインスタンスを検証する語彙が定義されている。Core仕様は、スキーマの構造、参照、評価の仕組みを定めている。
まず、利用する版を`$schema`で明示する。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object"
}版を明示しないと、ツールが異なるdraftとして解釈し、同じ記述でも挙動がずれる可能性がある。OpenAPI 3.1のSchema Objectは、JSON Schema Draft 2020-12を基礎にした方言を採用している。OpenAPIと単体のJSON Schemaを併用する場合も、どの方言を正とするかを最初に合わせたい。
API仕様書の書き方で整理した「5つの契約」のうち、JSON Schemaが主に担当するのは入出力の構造だ。認証、再試行、状態遷移などを全部押し込むものではない。機械検証できる条件と、人が判断する業務ルールを分けて設計する。
ステップ1:対象と必須条件を最小構成で書く
最初から全項目を盛り込まず、APIが受け取るオブジェクトの境界を固定する。経費申請の最小構成は次のように書ける。
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/schemas/expense-claim.json",
"title": "経費申請",
"type": "object",
"required": ["expenseDate", "amount", "category"],
"properties": {
"expenseDate": {
"type": "string",
"format": "date",
"description": "実際に支出した日"
},
"amount": {
"type": "integer",
"minimum": 1,
"description": "税込の日本円"
},
"category": {
"type": "string",
"enum": ["transport", "lodging", "other"]
}
},
"additionalProperties": false
}重要なのは、型より意味である。`amount`を`integer`にしても、通貨、税込・税抜、上限、返金時の扱いまでは伝わらない。機械検証する条件はキーワードで書き、背景は`description`や要求IDに残す。
`required`は「値が空でない」ではなく、プロパティが存在することを求める。`null`を許すなら型として明示する。空文字を禁止するなら`minLength`が必要だ。省略、`null`、空文字を同じ意味で扱わない。
また、`additionalProperties: false`は未定義項目を拒否する。入力ミスの早期発見に役立つ一方、将来の項目追加を既存クライアントが拒否する設計にもなり得る。要求側と応答側で方針を分け、互換性を判断する。
ステップ2:値の範囲と表記を制約する
次に、業務上受け入れられる値を制約へ変える。
| 業務条件 | 主なキーワード | 例 |
|---|---|---|
| 1円以上 | `minimum` | `"minimum": 1` |
| 文字数上限 | `maxLength` | `"maxLength": 200` |
| 候補を限定 | `enum` | 経費区分 |
| 配列件数 | `minItems`、`maxItems` | 添付は1〜5件 |
| 重複禁止 | `uniqueItems` | タグの重複を禁止 |
| 文字パターン | `pattern` | 社内管理番号 |
ここで、見た目の形式と業務上の正しさを混同しない。`format: "date"`は日付形式を示せても、「未来日は不可」「締め済み期間は不可」という現在状態に依存する判断までは表現しにくい。利用するバリデーターが`format`を検証として扱うかも確認が必要だ。
静的な構造検証はJSON Schema、データベースや権限を参照す る判断はアプリケーションの業務検証、と責任を分ける。どちらか一方で済ませると、同じルールを複数画面へ重複実装したり、スキーマだけ通る不正な申請を受け入れたりする。
データフロー図の書き方でデータの発生元と保管先を整理しておくと、どの時点で何を検証するか決めやすい。
ステップ3:条件分岐を業務ルールから組み立てる
実務の入力条件は一定ではない。たとえば、宿泊費なら宿泊先が必須、1万円以上なら領収書が必須、といった分岐がある。
Draft 2020-12では`if`、`then`、`else`で条件を表現できる。
{
"if": {
"properties": {
"category": { "const": "lodging" }
},
"required": ["category"]
},
"then": {
"required": ["hotelName"]
}
}条件を増やす場合は`allOf`で規則を並べられる。ただし、複雑な分岐を一つの巨大なスキーマへ詰め込むと、レビューもエラー表示も難しくなる。
分岐を書く前に、デシジョンテーブルで条件と結果を整理する。デシジョンテーブルの書き方で、宿泊費、金額、領収書、事前申請の組み合わせを洗い出し、そのうち入力だけで判定できる規則をJSON Schemaへ移す。
目安は明快だ。
- 入力JSONだけで合否が決まる:JSON Schema
- 利用者の役職や権限が必要:認可処理
- 締め状態や残予算が必要:業務サービス
- 人の例外承認が必要:ワークフロー
この境界を要求IDで結ぶと、AIに実装を依頼するときも「スキーマへ書く条件」と「サービス層へ書く条件」を分けられる。
ステップ4:部品を再利用し、変更範囲を追えるようにする
住所、金額、添付ファイルなどをAPIごとにコピーすると、修正漏れが起きる。`$defs`と`$ref`を使い、意味が同じ部品を再利用する。
{
"$defs": {
"fileId": {
"type": "string",
"minLength": 1,
"description": "アップロード済みファイルの識別子"
}
},
"properties": {
"receiptFileId": {
"$ref": "#/$defs/fileId"
}
}
}再利用単位は「string」ではなく、業務上の意味で切る。`employeeId`と`fileId`が同じ文字列形式でも、同じ部品に はしない。将来の変更理由が異なるからだ。
外部ファイルを参照する場合は、`$id`と参照先の解決方法を決める。スキーマを更新したのに、CIや実行環境が古い参照先を使えば検証結果がずれる。版、配布方法、キャッシュ、廃止手順を仕様に含める。
変更レビューでは、必須項目の追加、許容値の縮小、`additionalProperties`の変更を破壊的変更の候補として扱う。列挙値の追加も、クライアントが未知の値で停止するなら影響がある。スキーマの差分だけでなく、利用側の実装とテストを見て判断する。
ステップ5:成功例より失敗例を契約テストにする
完成したスキーマは文書として置くだけでなく、CIで検証する。少なくとも次の三種類を用意する。
- 通るべき最小入力
- 境界値を含む通る入力
- 理由を特定できる通らない入力
経費申請なら、0円、文字列の金額、未定義の区分、宿泊先なしの宿泊費、領収書なしの高額申請を失敗ケースにする。単に「invalidになる」だけでなく、どの項目のどの規則に違反したかを確認する。
| ケースID | 入力条件 | 期待結果 |
|---|---|---|
| SCH-01 | 金額1円 | 成功 |
| SCH-02 | 金額0円 | `minimum`違反 |
| SCH-03 | 宿泊費で宿泊先なし | `required`違反 |
| SCH-04 | 未定義項目あり | `additionalProperties`違反 |
| SCH-05 | 日付形式が不正 | 日付形式の検証に失敗 |
AIテスト自動化は受け入れ条件からで述べた通り、ツール導入より先に合否を固定する。バリデーターを変更したときも同じfixtureを 流し、挙動差を検出する。
さらに、APIテストではスキーマ検証の後に認証、権限、状態遷移、冪等性を確認する。JSON Schemaが通ったことは、業務処理が許可されたことを意味しない。
OpenAPI 3.1と組み合わせるときの確認点
OpenAPI 3.1はJSON Schema Draft 2020-12を基礎にしているため、API操作とデータ制約を近い場所で管理できる。OpenAPI SpecificationのSchema Objectには、方言とSchema Objectの扱いが定義されている。
レビューでは次を確認する。
- OpenAPIの版とJSON Schemaの方言が明示されているか
- 要求と応答で未知項目の扱いを決めたか
- `format`の検証方針をツール間で合わせたか
- 条件分岐がデシジョンテーブルと一致しているか
- 共通部品の所有者と変更手順があるか
- 正常系と異常系のfixtureをCIで実行しているか
- スキーマ外の認可・状態・再試行を別途テストしているか
JSON Schemaの目的は、長い仕様書を短くすることではない。曖昧な判断を見つけ、機械が確認できる条件へ変え、実装とテストで同じ契約を使うことだ。
APIの制約を業務要件と合わせて整理するツールを検討している場合は 、Kakusillの紹介ページも参考にしてほしい。
株式会社Atsumellでは、業務フロー、データ、API、権限、例外、受け入れ条件を整理し、AIと開発チームが実装に使える仕様へ落とし込む支援を行っている。APIの入力条件が議事録やコードへ散らばっている場合は、お問い合わせから相談してほしい。



