【実務・中級編】 ヘッダーバージョニング(Accept: application/vnd.api.v1+json)の設計思想 – Web APIアーキテクチャ・データ連携実践ガイド

URLを汚さず、進化を止める。「Acceptヘッダー」によるAPIバージョニングの美学

現場でAPI設計をしていると、一度は必ず突き当たる壁がある。「APIのバージョン管理、どうやる問題」だ。

api.example.com/v1/users というURLを叩き、数年後に v2 が出たら api.example.com/v2/users に移行する。これは確かに分かりやすい。だが、リソースの本質が「ユーザー」であることに変わりはないのに、URLという「名前」を変えることが、RESTの哲学として本当に正しいのだろうか?

今回は、RFC 7231が定義する「コンテントネゴシエーション(Content Negotiation)」を活用し、URLを固定したままスマートにバージョンを切り替える「ヘッダーバージョニング」の世界を深掘りしていこう。

—

なぜURLバージョニングは「負債」になり得るのか

URLに /v1/ を含める方式は、キャッシュサーバー(CDN)やプロキシにとって非常に都合が良い。しかし、それは「リソースの識別」というRESTの原則を少しばかり犠牲にしている。

一方、Accept ヘッダーによるバージョニングは、「同じリソースに対して、異なる表現(Representation)を要求する」という、HTTP本来の仕様に忠実なアプローチだ。

ヘッダーバージョニングの強み

  • URLの正規化: リソース(ユーザー情報など)のURIは永続的であり、バージョンの変更で変わることがない。
  • 疎結合な進化: クライアント側でヘッダーを差し替えるだけで、サーバー側のロジックを段階的に切り替えられる。

—

現場で使う Accept: application/vnd.api.v1+json の作法

この形式は、ベンダー固有のメディアタイプ(Vendor Tree)を利用した設計だ。IANAに登録された標準的な application/json ではなく、自社独自の拡張であることを明示する。

通信フロー(シーケンス)のリアル

1. クライアント: GET /users/123 を送信。ヘッダーに Accept: application/vnd.api.v1+json を付与。
2. サーバー(ロードバランサー/API Gateway): ヘッダーを解析し、バックエンドの適切なロジックへルーティング。
3. サーバー(アプリケーション): 要求されたバージョンに基づき、JSONの構造を変換してレスポンス。
4. クライアント: バージョンに応じたデータを受け取る。

—

実践:クライアントからのリクエスト例

実際に curl や Fetch API で実装する際は、以下のように記述する。

curlでのリクエスト例

# ヘッダーを明示的に指定して叩く
curl -X GET https://api.example.com/users/123 \
  -H "Accept: application/vnd.api.v1+json" \
  -H "Authorization: Bearer <token>"

JavaScript (Fetch API) での実装例

fetch('https://api.example.com/users/123', {
  method: 'GET',
  headers: {
    // サーバーに「v1のJSON構造をくれ」と伝える
    'Accept': 'application/vnd.api.v1+json',
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(data => console.log(data));

—

運用上の最大の敵:キャッシュとの戦い

ここがインフラ屋としての腕の見せ所だ。この方式を採用すると、CDN(CloudFrontやFastlyなど)のキャッシュ制御が複雑化する。

通常、CDNはURLだけでキャッシュキーを生成する。しかし、同じURLでも v1 と v2 では内容が異なる。この場合、キャッシュキーに Accept ヘッダーを含める設定(Vary: Accept)が不可欠だ。

Nginxでの設定例

もしあなたがNginxでAPIゲートウェイを組んでいるなら、以下のように Vary ヘッダーを適切に返すよう設定しなければならない。

location /users/ {
    # クライアントに「Acceptヘッダーによって内容が変わるよ」と教える
    add_header Vary "Accept";
    
    proxy_pass http://api_backend;
}

注意: Vary: Accept を付与すると、ブラウザやCDNが「Acceptヘッダーの組み合わせごとにキャッシュを作成」するため、キャッシュ効率が低下する可能性がある。トラフィックが膨大な環境では、このトレードオフを慎重に見極める必要がある。

—

シニアエンジニアからの助言

最後に、現場で泣きを見ないためのTipsを一つ。

「Accept ヘッダーを忘れたクライアントをどう扱うか?」

RFCでは、Accept が指定されない場合は「サーバーが最も適切と判断する表現」を返すことが許されている。しかし、本番環境で「いきなりv2に切り替えたら古いクライアントが全滅した」という事故を防ぐため、デフォルトは常に v1 を返すなどの「保守的なフォールバック」を設計に組み込んでおくこと。

また、APIのドキュメントには必ず「このAPIはContent-Negotiationを利用している」と明記し、Accept ヘッダーのサンプルを大きく載せておくこと。これを怠ると、後輩エンジニアが「なぜかv2のフィールドが取れない!」と深夜2時に泣きついてくることになる(笑)。

RESTは単なる規約ではなく、分散システムの調和のための「作法」だ。URLというインターフェースを美しく保つために、ぜひこのヘッダーバージョニングの知見を役立ててほしい。

コメント

タイトルとURLをコピーしました