REST APIのバージョニング: URL、ヘッダー、クエリのどれが合うか
1分で読めます
REST APIのバージョニングとは、リクエストがどの契約を使っているかをAPIが呼び出し元に伝える仕組みで、これによってサーバーは、まだ移行していない呼び出し元を壊さずに契約を変更できる。バージョンを置ける場所は三つある。URLパス(/v2/orders)、リクエストヘッダー(Accept: application/vnd.acme.v2+jsonや独自のApi-Version: 2026-10-01)、クエリパラメータ(/orders?version=2)だ。パスはどこからでも見えるので最も一般的で、ヘッダーはURLが最もきれいになり、クエリ文字列は多くのチームが後悔する方式だ。どれが合うかは、呼び出し元が誰で、変更をどう伝えるつもりかで決まる。
| バージョンの置き場所 | 例 | 誰に見えるか | デフォルトでキャッシュ可能か | 採用例 |
|---|---|---|---|---|
| URLパス | GET /v2/orders | すべてのログ、リンク、プロキシ、ブラウザ | 可能 | Twitter、Shopify、多くの公開API |
| 独自ヘッダー | Api-Version: 2026-10-01 | それを設定したクライアントのみ | Vary: Api-Versionがあれば | Stripe(Stripe-Version)、GitHub(X-GitHub-Api-Version) |
| Acceptヘッダー | Accept: application/vnd.acme.v2+json | それを設定したクライアントのみ | Vary: Acceptがあれば | GitHubのメディアタイプ、一部のエンタープライズAPI |
| クエリパラメータ | GET /orders?api-version=2 | ログとリンク。フィルタと混ざる | 可能だがキャッシュキーを汚す | AzureのREST API |
| バージョンなし | GET /ordersと追加的な変更のみ | 誰にも見えない | 可能 | 社内APIやGraphQL型のAPI |
REST APIのv1とv2とは何か
v1とv2は、APIの契約の二つのバージョンに付いたラベルだ。同じリソースが別のパスで到達でき、v2では、v1の呼び出し元が依存しているフィールド名、型、エンドポイント、エラーの形が変わっているかもしれない。/v1/ordersへのリクエストは、v1がサポートされている限り古い形を受け取り続け、/v2/ordersへのリクエストは新しい形を受け取る。この番号は互換性についての約束であり、その約束こそが番号を役立つものにしている。リリースのたびに上げていては、ラベルはノイズになる。
ほとんどのAPIが新しいメジャーバージョンを必要とするのは、既存の呼び出し元を壊す変更のときだけだ。フィールドの追加、新しいエンドポイント、新しいオプションのパラメータは現行バージョンのまま出荷できる。よく書かれたクライアントは、知らないものを無視するからだ。いつAPIをバージョン管理すべきか、代わりに何をすべきかはその判断を扱っている。この記事は、上げると決めた後にREST APIのバージョニングでバージョンをどこに置くかの話だ。
REST APIのバージョニングはURLとヘッダーのどちらに置くべきか
呼び出し元に、ブラウザでリクエストを打つ人、ドキュメントの例をコピーする人、ログを読む人が含まれるならURLに置く。リソースごとに安定したURLを一つ持ちたくて、呼び出し元がすべてSDKか自分が管理するコードならヘッダーに置く。URLパスは他のどの方式にもない形で明示的だ。リクエスト行が貼られたサポートチケットは、それだけでバージョンを教えてくれる。ヘッダーはすべてのリクエストで設定しなければならず、リンクからは見えず、プロキシで落とされやすい。ヘッダーでバージョン管理するAPIが、ヘッダーを送らないリクエストのために必ずデフォルトを定義しているのはそのためだ。
ヘッダー方式には、URLが整う以外に本物の利点が一つある。リソースの同一性とその表現を分離することで、これはHTTPの設計どおりの形だ。RFC 9110はAcceptによるコンテンツネゴシエーションを記述しており、application/vnd.github.v3+jsonのようなメディアタイプはそのやり方で表現したバージョンだ。実際には、設計の純粋さより、呼び出し元がヘッダーを送るのを忘れないかどうかのほうが重要だ。
日付ベースのバージョニングはどう動くか
v1やv2の代わりにバージョンを日付にし、破壊的変更のたびに新しい日付を付ける。呼び出し元は統合したときの日付を、通常はアカウント単位で一度固定し、自分で移行を選ぶまでその挙動を保つ。StripeのStripe-VersionヘッダーとGitHubのX-GitHub-Api-Versionはどちらもこの方式だ。
トレードオフは実装側にある。破壊的変更の一つひとつが、固定された日付と現在の形を相互変換する小さな変換層になり、サーバーはそれを積み重ねる。二年前に固定されたリクエストは、それ以降のすべての変更を通過する。ハンドラのコピーをもう一組持つより作るのは重く、めったに変わらないAPIほど報われる。月に一度破壊的変更を出すチームは、数十の変換を抱えることになる。StripeのAPIバージョニングは、チェンジログが何を載せなければならないかも含めて、このモデルを呼び出し元の側から見ている。
クエリ文字列のバージョニングはなぜ勧められないのか
契約とフィルタを混ぜてしまうからだ。/orders?status=open&api-version=2では、バージョンが、レスポンスのすべてのフィールドの形を決めるものではなく、任意の絞り込み条件のように見える。うっかり落としやすいのもこの方式だ。クエリ文字列はクライアントライブラリに組み立て直され、一部のプロキシに削られ、人の手で編集される。Microsoft自身のREST API設計ガイダンスはAzureが使うクエリパラメータ形式を文書化しており、それを実用的にしているのは周辺の生成SDKのエコシステムだ。そのツール群なしでは、パラメータを忘れた呼び出し元は黙ってデフォルトのバージョンを受け取る。
古いバージョンはどうなるか
停止する日付を決めた上で、動き続ける。バージョニングの目的は呼び出し元が自分のスケジュールで移行できるようにすることで、それが成り立つのは、古いバージョンが移行に十分な期間使え、いつ終わるかが伝えられている場合だけだ。GoogleのAPIバージョニングガイダンスは自社APIに使うライフサイクルを記述している。安定版は公開された非推奨化の日付までサポートされ、後継は非推奨化の期間中ずっと利用できる。
告知は、多くのチームが作り込みの足りない部分だ。新しいパスやヘッダーは、それが存在すること、何が変わったか、v1がいつなくなるかを知っている呼び出し元にしか役立たない。この三つの事実には、それぞれ置き場所が要る。フィールド単位の差分を記したAPIチェンジログの日付付きエントリ、終了日を明記した非推奨の通知、そして古いバージョンに残っている呼び出し元のために変更を一つずつ案内する移行ガイドだ。APIバージョンの廃止は最後の段階を扱っており、クライアントがすべてのレスポンスで日付を知れるSunsetヘッダーも含んでいる。
REST APIのバージョニングを完全に避けることはできるか
できる。すべての変更が追加的である限りは。フィールドは足すだけで、削除も改名もしない。エンドポイントは足すだけで、既存のものが返す内容は変えない。新しいオプションのパラメータは受け入れ、古いものの意味は変えない。多くの社内APIはこのルールだけで何年も動いており、GraphQLのスキーマが設計上従っているルールでもある。代償は、膨らむ一方の契約だ。非推奨のフィールドも、どこかの誰かがまだ読んでいるために残さなければならない。
追加的な変更が不可能なとき、バージョンが道具になる。パス、ヘッダー、日付のどれを選ぶかは、呼び出し元に何が見え、変更がどう告知されるかを決める。変わらないのは、呼び出し元が読む場所にその変更を書き残さなければならないことだ。外部の呼び出し元を持つAPIなら、その場所はチェンジログであり、何が破壊的変更にあたるかは破壊的変更の記事が扱っている。
FAQ
REST APIをバージョン管理する最良の方法は何か? URLパスのメジャーバージョンが最も広く理解されていて間違えにくく、公開APIのデフォルトになる。ヘッダーのバージョンはURLを安定させ、SDK主導のAPIに向く。日付ベースのヘッダーは、めったに変わらず、呼び出し元に挙動を固定させたいAPIに向く。
API URLのv1とは何か?
APIの契約の最初のメジャーバージョンだ。/v1/...へのリクエストは、別のフィールドや挙動を持つ/v2/が存在した後も、バージョン1として文書化された形を約束される。
変更のたびに新しいAPIバージョンが必要か? いいえ。新しいフィールド、エンドポイント、オプションのパラメータのような追加的な変更は、既存のバージョンのまま出荷できる。新しいバージョンが必要なのは、既存の呼び出し元を壊す変更だけだ。
古いAPIバージョンはどのくらいサポートすべきか? 呼び出し元が移行できるだけの期間で、公開APIなら通常、後継が利用可能になってから6か月から12か月だ。日付は告知時に公開し、非推奨の通知で繰り返す。
この記事の技術的な記述は第三者による確認を受けていません。誤りがあればお知らせください。修正します。