APIコントラクトとは?定義・実例・ベストプラクティス
すべての統合の失敗には、同じ根本原因の物語があります。チームAがエンドポイントを構築しました。チームBがそれを利用しました。「フィールド名はuserIdだ」と「実は今はuser_idだ」の間のどこかで、本番環境で何かが壊れ、2つのチームは午後を作戦会議室で、APIについての理解はどちらが正しいかを言い争って過ごしました。
解決策はより良いコミュニケーションではありません。より良い成果物です。APIコントラクトです。APIが何をするかについての、単一で形式的な合意された定義であり、両者がそれに対して構築し、検証し、互いに責任を持たせ合えるものです。
本ガイドは、APIコントラクトとは何か、異なるAPIスタイルに使われるフォーマット、コントラクトファースト対コードファースト開発、APIコントラクトテストがどう機能するか、そしてコントラクトを時間とともに信頼できるものに保つベストプラクティスを扱います。
APIコントラクトとは?
APIコントラクトとは、APIのインターフェースの形式的で合意された仕様です。それは次のものを、正確かつ曖昧さなく定義します。
- 操作 — APIが公開するエンドポイント、メソッド、クエリ、プロシージャ。REST APIならパスとHTTP動詞。gRPCならサービスとRPC。イベント駆動APIならチャネルとメッセージタイプ。
- リクエストとレスポンスのスキーマ — 交換されるデータの正確な形。フィールド名、型、必須か任意か、フォーマット、制約。
- エラーのセマンティクス — 失敗がどう見えるか。どのエラーコードが存在し、何を意味し、エラーレスポンスがどの構造に従うか。
- 認証と認可 — 呼び出し元がどう自分を識別するか。APIキー、OAuthスコープ、JWTクレーム、mTLS。
- バージョニングと安定性のルール — インターフェースのどの部分が安定しているか、どう変更が導入されるか、廃止予定がどう機能するか、プロバイダーがコミットする保証(レート制限、SLA)は何か。
キーワードは合意されたです。コントラクトは、コードが今日たまたまやることの記述ではありません。それはプロバイダーとその利用者の間のコミットメントです。「これがインターフェースであり、警告なしにそれを壊すことはしない。」そのコミットメントこそが、独立した開発を可能にするものです。バックエンドがまだ書かれている間に、フロントエンドチームはコントラクトに対して構築できます。パートナーは、あなたのソースコードを読まずに統合できます。
OpenAPIファイルからクライアントSDKを生成したり、仕様からサービスをモックしたり、公開されたスキーマを壊すからとプルリクエストを却下したことがあるなら、あなたはAPIコントラクトを本来意図された使い方、すなわちインターフェースの真実の源として使ったことがあります。
APIコントラクトのフォーマット:APIスタイルごとに1つ
普遍的なコントラクトフォーマットはありません。なぜなら、普遍的なAPIスタイルがないからです。各プロトコルファミリーは、独自の仕様標準に収束してきました。
REST / HTTP APIのためのOpenAPI
OpenAPI(旧Swagger)は、HTTP APIの支配的なコントラクトフォーマットです。OpenAPIドキュメントは、パス、操作、パラメータ、リクエストボディ、レスポンススキーマ、認証スキーム、サーバーを、すべてYAMLまたはJSONで記述します。
paths:
/orders/{orderId}:
get:
summary: Get an order by ID
parameters:
- name: orderId
in: path
required: true
schema:
type: string
format: uuid
responses:
"200":
description: The order
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
"404":
description: Order not found
OpenAPIを取り巻くエコシステムこそが、その本当の強みです。インタラクティブなドキュメントビューア、クライアントとサーバーのコードジェネレーター、モックサーバー、バリデーター、リンターが、すべて同じファイルを消費します。
gRPCのためのProtocol Buffers
gRPC APIは、Protocol Buffersを使って.protoファイルで定義されます。protoファイルこそがコントラクトです。サービス、RPCメソッド、強く型付けされたメッセージを定義し、クライアントとサーバーの両方のコードがそれから生成されます。
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
}
message GetOrderRequest {
string order_id = 1;
}
gRPCではコード生成が必須なので、仕様と実装の間のコントラクトのドリフトは、RESTよりも構造的に起きにくいです。番号付きのフィールドも明示的な進化ポリシーをエンコードします。フィールドを追加できますが、番号を振り直したり用途を変えたりすると互換性が壊れます。
GraphQL APIのためのGraphQL SDL
GraphQLは、コントラクトがプロトコル自体に組み込まれています。スキーマ定義言語(SDL)は、APIがサポートするすべての型、クエリ、ミューテーション、サブスクリプションを記述し、サーバーがそれを強制します。スキーマに一致しないリクエストは、どのリゾルバが実行されるよりも前に拒否されます。イントロスペクションは、利用者が常にライブAPIから現在のコントラクトを取得できることを意味します。
イベント駆動APIのためのAsyncAPI
非同期API(Kafkaトピック、RabbitMQキュー、NATSサブジェクト、WebSocket)は、何年もドキュメントの無法地帯でした。AsyncAPIは、OpenAPIのアプローチをイベント駆動システムに適応させることで、それを変えました。AsyncAPIドキュメントは、チャネル、そこでの操作(送信/受信)、メッセージペイロード、ブローカーのバインディングを記述します。「誰が何を発行し、誰がそれを消費するか?」が日々の問いであるアーキテクチャにとって、AsyncAPIコントラクトは、答えと考古学プロジェクトの違いです。
AIエージェントのためのMCPツールスキーマ
最も新しいコントラクトのタイプは、サービス間のインターフェースをまったく記述しません。Model Context Protocol(MCP)は、サービスがAIエージェントにツールを公開できるようにし、各ツールには名前、説明、そして入力のためのJSON Schemaが付きます。そのツールリストは正真正銘のAPIコントラクトです。おそらくより高いリスクを伴うものです。なぜなら、自律的なエージェントがあなたのシステムに対して何をすることが許されるかを定義するからです。私たちはMCPツールをAPIコントラクトとして扱うことと、それらがRESTエンドポイントと同じドキュメントの厳密さに値する理由について、深く書いています。
要点:あなたのAPIスタイルが何であれ、それに対応する機械可読なコントラクトフォーマットが存在します。モダンなシステムはたいてい一度に複数を必要とします。公開APIにはREST、内部にはgRPC、イベントにはAsyncAPI、エージェントにはMCP。これこそが、コントラクトが5つの散らばったリポジトリよりも単一の住処から恩恵を受ける理由です。
コントラクトファースト vs コードファースト開発
コントラクトが生まれる方法は2つあり、その選択がAPIワークフロー全体を形作ります。
コントラクトファースト(デザインファースト)
コントラクトファースト開発では、実装を書く前に仕様を書きます。OpenAPIファイルやproto定義が設計され、レビューされ、合意され、それから両者のプロバイダーと利用者がそれに対して、しばしば並行して構築します。
利点:
- 並行開発。 利用者は、プロバイダーが実装する間、クライアントを生成しモックに対して構築できます。誰も待ちません。
- コードレビューの前のデザインレビュー。 出荷されたエンドポイントをリファクタリングするより、YAMLのdiffでフィールド名を言い争う方がはるかに安上がりです。
- 一貫性。 コントラクトを意図的な成果物として設計することで、命名規則、ページネーションのパターン、エラーフォーマットをAPI横断で強制するのが自然になります。
- 利用者への焦点。 既存のデータモデルに最も簡単にくっつくインターフェースではなく、利用者が必要とするインターフェースを設計します。
欠点:
- 前倒しのプロセスが増える。内部エンドポイントをイテレートする2人のチームにとって、形式的な設計フェーズはオーバーヘッドになり得ます。
- 実装がコントラクトに対して検証されないとドリフトのリスクがある。両者を正直に保つにはツール(検証ミドルウェア、CIチェック)が必要です。
コードファースト
コードファースト開発では、実装を書き、そこからコントラクトを生成します。アノテーション、リフレクション、フレームワークのイントロスペクションがOpenAPIドキュメントやGraphQLスキーマを生み出します。
利点:
- 小さなチームのためのスピード。 別の設計ステップがなく、コントラクトは常にコードから導出可能です。
- 構造上ドリフトがない。 生成された仕様は実装に一致します。なぜなら、それは実装から来るからです。
欠点:
- コントラクトがコミットメントではなく副産物になる。コードがやることが何であれ、それがAPIです。偶然の部分も含めて。
- 破壊的変更が容易にすり抜ける。インターフェースをインターフェースとしてレビューすることを何も強制しないからです。
- 生成された仕様はしばしば平凡です。説明が欠落し、エラーのドキュメントが曖昧で、例がありません。
どちらを使うべきか?
実用的な経験則:APIの利用者が多く、それらをコントロールできないほど、コントラクトファーストが報われる。 公開API、パートナー統合、別々のチーム間のコントラクトは、コントラクトファーストの扱いに値します。同じチームが所有する1つのフロントエンドが消費する内部エンドポイントは、コードファーストでよいでしょう。生成されたコントラクトが依然として公開され、バージョン管理され、破壊的変更がチェックされる限りは。
多くの成熟したチームはハイブリッドに落ち着きます。スピードのためにコードファースト、そしてコントラクトファーストの安全性の大半を与えるコントラクトレベルのCIゲート(破壊的変更の検知、スキーマのリンティング)を組み合わせます。
APIコントラクトテスト
何も検証しないコントラクトは願望です。APIコントラクトテストは、プロバイダーと利用者が実際に合意されたインターフェースに準拠していることを自動的にチェックする実践です。3つの技法が支配的です。
コンシューマー駆動コントラクトテスト
Pactによって普及したコンシューマー駆動コントラクトテストでは、各利用者が依存する具体的な相互作用を記録します。「/orders/123をGETしたら、id、status、totalを含むボディとともに200を期待する。」これらの記録された期待がコントラクトを形成し、それがプロバイダーのCIパイプラインでプロバイダーに対して再生されます。
このアプローチの威力は精度です。プロバイダーは、各利用者が実際にどのフィールドを使うかを正確に学びます。フィールドを削除したい?コントラクトテストが、いずれかの利用者が壊れるかどうかを即座に教えてくれます。デプロイした後ではなく、前に。
CIでのスキーマ検証
よりシンプルで広範な技法:実装が公開された仕様に一致することを検証します。
- サービスに対してリクエストを実行し、レスポンスをOpenAPIスキーマに対して検証する。
- コントラクトに準拠しないあらゆるレスポンスを拒否する検証ミドルウェアを使う(ステージングで最適)。
- 完全性とスタイルのために仕様自体をリントする(Spectralなどのツール)。
これは最も一般的な失敗モード、すなわち仕様が一つのことを言い、コードが別のことをする、を安価かつ継続的に捕まえます。
破壊的変更の検知
最後に、コントラクト自体をdiffします。oasdiff(OpenAPI)、Buf(protobuf)、GraphQL Inspectorのようなツールは、仕様の新しいバージョンを以前のものと比較し、各変更を分類します。追加的(安全)か、破壊的(フィールド削除、型変更、新しい必須パラメータ)か。これをCIに組み込めば、破壊的変更は、利用者への静かなサプライズではなく、明示的で意図的な承認を要するビルド失敗になります。
このセクションから1つだけやるなら、これをやってください。破壊的変更の検知はセットアップが安価で、最も痛い失敗を捕まえます。
なぜAPIコントラクトはアーキテクチャドキュメントに属するのか
ほとんどのチームが見逃す部分がここにあります。美しいOpenAPIファイル、厳密なPactスイート、CIの破壊的変更ゲートを持っていても、何かを変える必要があるときに重要な問いに答えられないことがあります。「誰がこのコントラクトに依存しているか?」
リポジトリ内のコントラクトファイルはインターフェースを記述しますが、そのコンテキストについては何も言いません。どのサービスがそれを実装するか?どのサービス、フロントエンド、パートナーがそれを消費するか?このエンドポイントを廃止したら、実際に何が壊れるか?その知識はたいてい人々の頭の中に存在し、それは誰かがチームを移るたびに劣化することを意味します。
ここでアーキテクチャドキュメントとAPIコントラクトは互いを必要とします。
- アーキテクチャのコンテキストのないコントラクトは、見えないうちに陳腐化する。 去年書き直されたサービスを記述する孤児の
openapi.yamlに誰も気づきません。それが記述するシステムに、何もそれを結びつけていないからです。 - コントラクトのないアーキテクチャ図は不正確である。 2つのボックスの間の「REST/JSON」とラベル付けされた矢印は、関係が存在することを教えますが、そこを何が流れるかは教えません。コントラクトこそが、矢印に意味を与えるものです。
C4モデルは、この接続のための自然な構造を提供します。コントラクトは、それを実装し消費するコンテナとコンポーネントに付随します(それらの用語の簡単な復習はC4モデルの用語集の項目をご覧ください)。API GatewayコンテナはそのOpenAPIコントラクトを運びます。内部マイクロサービスはそのprotoファイルを運びます。Kafka中心のサービスは、そのチャネルを定義するAsyncAPIドキュメントを運びます。
これこそがArchylのAPIコントラクト機能が機能する方法です。OpenAPI、gRPC、GraphQL、AsyncAPI、MCPのコントラクトを(gitから同期するか直接貼り付けて)インポートし、アーキテクチャモデルのC4要素にリンクします。リンクは双方向です。コントラクトからは、どの要素がそれを実装し消費するかが見え、図上のどの要素からも、そのインターフェースを記述する実際の仕様を開けます。コントラクトが変わったとき、暗黙知から依存関係の図を再構築する代わりに、アーキテクチャのどの部分が影響範囲にあるかを一目で見られます。この機能についてはAPIコントラクト:アーキテクチャにリンクされたAPI仕様で詳しく扱いました。
この原則は、ツールに関係なく成り立ちます。コントラクトは、誰も開かないフォルダの中ではなく、それが結びつけるアーキテクチャ要素の隣に存在するときに最も価値があります。
APIコントラクトのベストプラクティス:チェックリスト
コントラクトは長命なコミットメントなので、それにふさわしく扱ってください。
- 単一の真実の源を確立する。 コントラクトごとに1つの正準の場所。仕様が3か所に存在するなら、それは0か所に存在します。それがgitリポジトリであれArchylのようなアーキテクチャプラットフォームであれ、権威あるバージョンがどこに存在するかを全員が知らねばなりません。
- 明示的にバージョン管理する。 すべてのコントラクトにバージョンを与え、バージョンの上昇が何を意味するかを定義する。セマンティックバージョニングがよく機能します。追加的な変更はマイナーバージョンを上げ、破壊的変更はメジャーバージョンを上げます。
- メジャーバージョンなしに決して壊さない。 フィールドの削除、型の変更、必須パラメータの追加、検証の厳格化 — すべて破壊的です。それらは新しいメジャーバージョンか新しいエンドポイント、そして移行パスを必要とします。
- 廃止ポリシーを書き、それを守る。 仕様で廃止予定の操作をマークし、サンセット日を伝え、利用者に現実的な猶予(日数ではなく月数)を与え、削除前に利用状況を監視する。
- コントラクトの変更をコードの変更のようにレビューする。 スキーマのdiffは、少なくとも実装のdiffと同じくらいの精査に値します。より多くの利用者を持つからです。
- 強制を自動化する。 CIでのスキーマ検証と破壊的変更の検知。人間がコントラクトに合意し、機械がそれを強制します。
- ハッピーパスだけでなく、エラーと認証もドキュメント化する。 400番台と401番台こそ、利用者がデバッグ時間を費やす場所です。それらを仕様化してください。
- コントラクトをアーキテクチャにリンクする。 すべてのコントラクトは、それを実装するコンポーネントと消費するコンポーネントまで追跡可能であるべきです。影響分析が調査ではなくルックアップになるように。
よくある質問
APIコントラクトとAPIドキュメントの違いは何ですか?
APIドキュメントは人間のために書かれます。ガイド、チュートリアル、例、概念の説明。APIコントラクトは、人間とツールの両方が消費する形式的で機械可読な仕様です。コードを生成し、リクエストを検証し、モックを駆動し、CIビルドを失敗させられます。良いドキュメントはしばしばコントラクトから生成されますが、コントラクトが拘束力のある成果物です。ドキュメントはAPIを記述し、コントラクトはそれを定義します。
コントラクトファースト開発とは何ですか?
コントラクトファースト(またはデザインファースト)開発とは、API仕様(OpenAPIドキュメント、protoファイル、GraphQLスキーマ)を実装する前に書き、合意することを意味します。それから利用者とプロバイダーが、同じ合意されたインターフェースに対して並行して構築します。設計の議論を前倒しにし、並行作業を可能にし、コントラクトをコードの副産物ではなく意図的なコミットメントにします。
APIコントラクトテストとは何ですか?
APIコントラクトテストは、プロバイダーと利用者が合意されたインターフェースに準拠していることを自動的に検証します。コンシューマー駆動コントラクトテスト(Pact風で、利用者の期待がプロバイダーに対して再生される)、CIでのスキーマ検証(実装が仕様に一致するかチェックする)、破壊的変更の検知(仕様のバージョンをdiffしてリリース前に非互換な変更をフラグする)を含みます。
内部APIにもコントラクトは必要ですか?
はい — おそらくより必要です。なぜなら、内部APIはより速く変わり、より少ない儀式で守られているからです。コントラクトはより軽量でよい(コードファーストの生成で問題ありません)ですが、それでも公開され、バージョン管理され、破壊的変更がチェックされるべきです。API変更によって引き起こされる本番インシデントのほとんどは、内部API変更によって引き起こされます。
アーキテクチャの内側にAPIコントラクトの住処を与える準備はできましたか?ArchylのAPIコントラクト機能を探索しましょう — OpenAPI、gRPC、GraphQL、AsyncAPI、MCPのコントラクトを、あなたのC4モデルにリンク。あるいは読み進めてください:APIコントラクト:アーキテクチャにリンクされたAPI仕様 | MCPツールをAPIコントラクトとして | C4モデルとは?完全ガイド。