API仕様書の書き方|5つの契約

目次
API仕様書を開くと、URL、HTTPメソッド、パラメータ、レスポンス例が並んでいる。見た目は整っている。ところが実装を始めると、質問が次々に出てくる。
「同じ登録要求が2回来たらどうするのか」「この項目は管理者だけが見られるのか」「タイムアウト後は再送してよいのか」「廃止予定の項目をいつ消せるのか」。
答えが別の議事録やチャットに散らばっているなら、そのAPI仕様書はまだ完成していない。API仕様書は入出力の一覧ではなく、システム同士と開発当事者の間に置く実行可能な契約である。
ここでは、API仕様書の書き方を5つの契約に分ける。題材は、経費申請を登録するAPIだ。
API仕様書はコードを書く前の合意点である
OpenAPI Specification 3.1.1では、APIのパス、操作、リクエスト、レスポンス、セキュリティなどを構造化して記述できる。形式をそろえれば、ドキュメント表示だけでなく、モック、クライアント生成、テストにもつなげやすい。
ただし、YAMLを書けば自動的に良い仕様になるわけではない。
たとえば、次の記述だけでは業務判断が不足している。
\`\`\`yaml
paths:
/expense-claims:
post:
summary: 経費申請を登録する
responses:
"201":
description: 登録成功
\`\`\`
これで分かるのは、登録先と成功時の状態だけだ。誰が登録できるか。申請番号は誰が採番するか。同じ申請が二重に届いたらどうするか。会計期間が締まっていたら何を返すか。ここまでは読めない。
API設計では、画面側、サーバー側、外部連携先、運用担当が同じ挙動を想像できるところまで決める。データフロー図の書き方で整理したデータの発生元・処理・保管先を、API単位の合意へ具体化する作業だ。
契約1:対象リソースと操作の意味を決める
最初に決めるのはURLではない。業務上の対象と、その状態をどう変えるかである。
経費申請なら、単なる「データ登録」では足りない。申請には下書き、申請済み、承認 済み、差し戻し、取消といった状態がある。操作によって許される状態遷移も違う。
| 項目 | 決める内容 | 経費申請の例 |
|---|---|---|
| リソース | 業務上、同一性を持つ対象 | 経費申請 |
| 識別子 | 誰がいつ採番するか | サーバーが申請IDを採番 |
| 操作 | 何を取得・作成・変更するか | 下書き作成、申請、承認 |
| 事前条件 | 操作前に満たす状態 | 下書きだけ申請できる |
| 事後条件 | 成功後 に保証する状態 | 申請済みになり受付時刻が残る |
\`POST /expense-claims\` に「申請」まで含めるのか、下書き作成だけにするのか。この違いは小さく見えるが、承認フロー、通知、取消、監査ログへ波及する。
URLやメソッドを先に並べると、同じ業務操作が複数のAPIに重複しやすい。業務イベントと状態遷移を先に置き、APIはその実現手段として割り当てる。
契約2:入力と出力の意味を項目単位で固定する
次はデータの契約だ。型と必須・任意だけでは不十分である。
\`amount: number\` と書いても、税込か税抜か、通貨は何か、負数を許すか、小数点以下を何桁まで持つかは分からない。\`date: string\` も、利用日か申請日か、タイムゾーンを含むかで意味が変わる。
各項目には少なくとも次を持たせる。
- 業務上の意味
- データ型と形式
- 必須になる条件
- 許容範囲と禁止値
- 省略時の扱い
- 個人情報・機密情報の区分
- 元データと更新責任者
経費申請の入力なら、次のように書ける。
| 項目 | 型 | 条件 | 業務ルール |
|---|---|---|---|
| \`expenseDate\` | \`string(date)\` | 必須 | 未来日は不可。締め済み期間は受付不可 |
| \`amount\` | \`integer\` | 必須 | 日本円、税込、1円以上 |
| \`receiptFileId\` | \`string\` | 1万円以上で必須 | 本人が参照権限を持つファイルだけ指定可 |
| \`costCenterId\` | \`string\` | 必須 | 申請日時点で有効な部門に限る |
出力も同じだ。後続処理に必要なら、保存された値に加えて、サーバーが確定した状態、版、処理時刻を返す。追跡IDは本文またはヘッダーで提供する。利用側が「何を正としてよいか」を判断し、障害時に処理を追えるようにするためだ。
OpenAPIのschemaは、この契約を機械で検査する土台になる。一方、業務上の意味や条件分岐はdescription、examples、別の業務ルール表で補う。機械可読と人間可読を競わせず、同じ要求IDで結ぶ。
契約3:認証・権限・データ範囲を分けて書く
「OAuth 2.0を使う」と書いただけでは、権限設計は終わらない。認証は誰かを確認する仕組みであり、認可はその主体が何をしてよいかを決める仕組みだ。
API仕様書では、次の4点を分ける。
- 実行主体:利用者、サービスアカウント、外部システムのどれか
- 操作権限:参照、作成、申請、承認、取消のどこまで許すか
- データ範囲:本人分、所属部門分、全社分のどこまで見えるか
- 監査情報:誰が、誰の権限で、何を実行したか何年間残すか
経費申請者が自分の申請を作れることと、上長が部下の申請を承認できることは 別の権限だ。代理申請があるなら、依頼者と実行者も分けて記録する。
OpenAPIの\`securitySchemes\`と各operationの\`security\`は認証方式や必要スコープを表現できる。しかし「部門長は自部門だけ承認可能」といった行レベルの条件は、権限表と受け入れテストに落とす必要がある。
APIキーを持っているだけで全件更新できる設計は、開発中は楽でも本番で詰まる。認証方式を選ぶ前に、業務上の権限境界を決める。
契約4:失敗・再試行・冪等性を正常系と同じ重さで決める
API連携の手戻りは、成功レスポンスより失敗時に起きやすい。
「400を返す」だけでは利用側は直せない。入力不備なのか、業務ルール違反なのか、権限不足なのか、競合なのか、サーバー障害なのかを識別できる必要がある。
エラー契約には次を含める。
| 項目 | 例 |
|---|---|
| HTTPステータス | \`409 Conflict\` |
| 業務エラーコード | \`ACCOUNTING_PERIOD_CLOSED\` |
| 利用者向けメッセージ | 対象月は締め処理済みです |
| 修正対象 | \`expenseDate\` |
| 再試行可否 | 条件を直すまで不可 |
| 追跡ID | 運用問い合わせで使うID |
GoogleのAPI改善提案にあるエラー設計も、機械が扱うエラーコードと、人が読むメッセージ、追加情報を分けている。エラーは末尾の補足ではなく、操作契約の一部だ。
さらに重要なのが冪等性である。RFC 9110では、同じ要求を複数回送っても意図したサーバー上の効果が一度と同じになる性質を定義している。
通信が切れたとき、利用側は処理が成功したのか判断でき ない。登録APIをそのまま再送すると、経費申請が二重に作られる恐れがある。そこで、次を仕様にする。
- 再試行できる操作と、できない操作
- 冪等キーの発行者、形式、有効期間
- 同じキーで内容が異なる要求を受けた場合の応答
- タイムアウト、\`429\`、\`5xx\`ごとの待機と再試行回数
- 非同期処理の状態確認方法
「失敗したら再送する」では粗すぎる。誰が、どの条件で、何回まで再送し、最後にどこへ戻すかまで決める。
契約5:変更・廃止・受け入れテストを同じ版で管理する
API仕様書は初回リリースで完成しない。項目追加、意味変更、利用制限、認証方式の変更が続く。
変更管理では、版番号だけでなく互換性を判定する。
- 任意項目の追加
- 列挙値の追加
- 必須項目の追加
- 項目の意味変更
- エラーコードの追加
- 既存パスの廃止
利用側が未知の列挙値で停止する実装なら、列挙値追加も破壊的変更になり得る。「サーバー側では互換」と判断せず、利用側の受け入れ条件で見る。
APIごとに、変更日、影響する利用者、移行期限、並行稼働期間、ロールバック条件を残す。廃止予定は文書だけでなく 、レスポンスヘッダーや利用状況の監視と組み合わせる。
そして、仕様とテストを分離しない。各契約に受け入れケースを結ぶ。
| 契約 | 受け入れテストの例 |
|---|---|
| リソース・操作 | 下書き以外は申請できない |
| 入出力 | 締め済み日付、0円、無効部門を拒否する |
| 権限 | 他部門の申請は参照も承認もできない |
| 失敗・再試行 | 同じ冪等キーの再送で重複登録しない |
| 変更・廃止 | 旧版利用者へ移行期限前に警告できる |
APIテスト自動化は受け入れ条件からで扱ったように、ツールを選ぶ前に合否を固定する。OpenAPIの構文検証、スキーマ検証、契約テスト、業務シナリオテストを重ねると、仕様書が実装から離れにくい。
OpenAPIと5つの契約を一枚の追跡表にする
API仕様書が複数ファイルに分かれていても、要求からテストまで追える表を一枚持つとレビューしやすい。
| 要求ID | 業務イベント | API操作 | 権限 | 主な失敗 | 再試行 | 受け入れケース |
|---|---|---|---|---|---|---|
| EXP-01 | 経費下書きを作る | \`POST /expense-claims\` | 本人 | 入力不備 | 冪等キーで可 | API-T01〜T04 |
| EXP-02 | 経費を申請する | \`POST /expense-claims/{id}:submit\` | 本人 | 締め済み、状態競合 | 条件修正後のみ | API-T05〜T08 |
| EXP-03 | 経費を承認する | \`POST /expense-claims/{id}:approve\` | 承認者 | 権限不足、状態競合 | 自動再試行不可 | API-T09〜T12 |
この表があると、発注者は業務ルールを確認できる。SIerは実装範囲と例外を見積もれる。フロントエンドとバックエンドは同じモックで並行開発できる。運用担当は追跡IDと再試行条件を使って障害を切り分けられる。
AIが読める仕様書の書き方と同じく、AIへ渡す場合も構造が効く。エンドポイント名だけでコードを生成させるのではなく、要求ID、権限、エラー、停止条件、テストを一緒に渡す。AIが補完してよい範囲と、人間が合意すべき範囲を分けられる。
API仕様書レビューのチェックリスト
最後に、レビューで使える問いを並べる。
- 対象リソースと状態遷移を業務担当者が説明できるか
- 各項目の意味、単位、必須条件、禁止値が決まっているか
- 実行主体、操作権限、データ範囲を分けているか
- 業務エラーを利用側が識別し、修正できるか
- タイムアウト後に再送してよい操作が明記されているか
- 二重登録を防ぐ方法と保持期間が決まっているか
- 互換性を壊す変更の判定基準があるか
- 廃止までの移行期間と監視方法があるか
- 各要求から受け入れテストを追跡できるか
- 仕様、実装、モック、テストが同じ版を参照しているか
全部に答えられれば、API仕様書は「読める資料」から「開発を進める契約」へ変わる。
株式会社Atsumellでは、業務フローの整理からAPI、データ、権限、例外、受け入れ条件まで、実装に渡せる要件定義を支援している。システム間連携の仕様がチャットや議事録に散らばっている場合は、お問い合わせから相談してほしい。



