APIのバージョニング方式について、代表的な4つのアプローチを比較する。
はじめに
APIに後方互換性のない変更を加えるとき、既存のクライアントを壊さずに新しい仕様へ移行するための仕組みがバージョニングである。
バージョンをどこに表現するかによって、方式は大きく次の4つに分けられる。
- パスベース
- クエリパラメータベース
- ヘッダーベース
- メッセージのペイロードベース
なお、バージョン番号の形式(セマンティックバージョニングや日付ベースなど)は、配置方法から独立した選択である。
本記事では、それぞれの特徴とトレードオフを端的にまとめる。
パスベース
URLのパスにバージョンを埋め込む方式である。
GET /v1/users
GET /v2/users
バージョンがURLに明示されるため可視性が高い。
ルーティングやキャッシュの制御がやりやすく、ブラウザやドキュメントからも辿りやすい。
APIゲートウェイやリバースプロキシでバージョンごとに振り分け先を変えやすく、CDNでもURL単位で素直にキャッシュできる。
一方で、本来は同じリソースを指すはずのURIがバージョンごとに変わってしまう。
API全体に共通のバージョンプレフィックスを付ける設計では、一部のリソースを変えるだけでもAPI全体のバージョンを上げることになりがちで、実装が重複しやすい。
クエリパラメータベース
クエリパラメータでバージョンを指定する方式である。
GET /users?version=1
ベースとなるURIを変えずに済み、パラメータを省略したときの既定バージョンを定義しやすい。
導入も手軽だが、指定が任意になりやすく、付け忘れによる意図しない挙動を招きやすい。
パスベースを前提とするAPIゲートウェイではクエリ単位の振り分けが複雑になりやすく、キャッシュキーにクエリを含めないと古い応答を返すおそれもある。
ヘッダーベース
HTTPヘッダーでバージョンを伝える方式である。
GET /users
X-API-Version: 1
なおX-接頭辞はRFC 6648で非推奨とされており、API-Versionのように接頭辞なしで命名することもある。
コンテントネゴシエーションとしてAcceptヘッダーにバージョンを埋め込む流儀もある。
GET /users
Accept: application/vnd.example.v2+json
URIをリソースの識別だけに保てる点が特徴で、バージョンをリソースとは別のメタ情報として扱える。
Acceptヘッダーによるメディアタイプ指定では、リクエストごとに受け取る表現を細かく切り替えられる。
GitHubのREST APIは、AcceptヘッダーのメディアタイプとX-GitHub-Api-Version(日付ベース)の両方でバージョンを扱っている。
反面、バージョンがURLに現れないため発見しづらく、ブラウザでの手動確認やデバッグがしにくい。
キャッシュではVaryヘッダーの扱いが必要になるなど、運用面の考慮が増える。
メッセージのペイロードベース
リクエストやメッセージの本文にバージョンを表すフィールドを持たせる方式である。
{
"version": "2",
"data": {}
}
トランスポート層に依存しないため、HTTPに限らずgRPCやメッセージキュー(Kafka・AMQPなど)のようにURLを持たない通信でも適用できる。
非同期メッセージングやイベント駆動の文脈では、バージョン情報がデータと一緒に運ばれる点が扱いやすい。
ただし、gRPC(Protobuf)はフィールド番号による互換的なスキーマ進化、KafkaはSchema Registryでの互換管理が一般的で、本文にバージョンフィールドを持たせる方式が主流とは限らない。
ルーティングやバリデーションの前に本文を解析する必要があり、性能面のオーバーヘッドになりやすい点にも注意したい。
本文を持たないGETリクエストには適用しづらく、設計の一貫性を欠きやすい。
比較表
| 観点 | パス | クエリパラメータ | ヘッダー | ペイロード |
|---|---|---|---|---|
| 可視性・発見性 | 高い | 中程度 | 低い | 低い |
| URIの純粋さ | 低い | 中程度 | 高い | 該当しない |
| キャッシュ制御 | しやすい | 注意が必要 | Varyが必要 |
困難 |
| ゲートウェイでの振り分け | 容易 | やや複雑 | 普通 | 困難 |
| 手動テスト・デバッグ | 容易 | 容易 | やや手間 | やや手間 |
| 主な用途 | 公開REST API | 軽量な切り替え | 純粋なREST設計 | 非同期・イベント駆動 |
まとめ
公開Web APIでは、多くのAPIゲートウェイやCDN、ロードバランサーがパスベースのルーティングを前提とするため、運用コストを抑えたいならパスベースが扱いやすい。
URIを汚さない設計を重視するならヘッダーベース、軽量な切り替えで十分ならクエリパラメータベースが候補になる。
非同期メッセージングやイベント駆動のシステムではペイロードベースが自然だが、HTTP APIではGETとの相性や性能の観点から、他の方式で解決できない場合に限るのが無難である。
いずれの方式でも、まずは後方互換を保てる追加的な変更で対応し、互換性を壊す変更が避けられないときにのみバージョンを上げる、という原則は変わらない。
方式を一つ選んで一貫して運用し、非推奨化の方針もあわせて決めておくとよい。