APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け

kento_morota 17分で読めます
社内のAI・IT活用、技術がわかる相手に無料相談。 相談 →
目次

APIに新機能を追加したいが、既存のクライアントを壊したくない——APIを長期運用する上で、バージョニング戦略の設計は避けて通れない課題です。適切な戦略なしにAPIを変更すると、連携先のシステムが突然動かなくなる深刻な事態を招きます。

本記事では、APIバージョニングの主要な方式の比較から、破壊的変更の定義、バージョン移行戦略、廃止プロセスの設計まで実践的に解説します。

APIバージョニングが必要な理由

なぜAPIにバージョニングが必要なのか、その背景を理解しましょう。

APIは契約である

APIは、サーバー(提供者)とクライアント(利用者)の間の「契約」です。クライアントは「このエンドポイントにこの形式でリクエストすれば、この形式のレスポンスが返ってくる」という前提でコードを書いています。この契約を一方的に変更すれば、クライアント側のシステムが破綻します。

破壊的変更とは何か

APIの変更には「破壊的変更(Breaking Change)」と「非破壊的変更(Non-Breaking Change)」があります。

破壊的変更の例

  • エンドポイントのURLを変更する
  • 必須パラメータを追加する
  • レスポンスのフィールドを削除・名前変更する
  • レスポンスのデータ型を変更する(string → integer など)
  • HTTPステータスコードの意味を変更する
  • 認証方式を変更する
  • エラーレスポンスの形式を変更する

非破壊的変更の例

  • オプショナルなパラメータを追加する
  • レスポンスに新しいフィールドを追加する
  • 新しいエンドポイントを追加する
  • エンドポイントの内部実装を変更する(外部仕様が同じであれば)
  • パフォーマンスの改善

バージョニングが必要なのは、破壊的変更を安全に導入するためです。非破壊的変更であれば、バージョンを上げずに既存のAPIに追加できます。

バージョニングを避ける努力

バージョニングはコストがかかるため、まずはバージョニングを避ける設計を心がけましょう。

拡張可能な設計
最初からレスポンスにメタデータフィールドを用意する、フィールド名を汎用的にするなど、将来の変更に対応しやすい設計を行います。

Tolerant Reader パターン
クライアント側で「知らないフィールドは無視する」というルールを徹底します。サーバーが新しいフィールドを追加しても、クライアントが壊れません。

Robustness Principle(ポステルの法則)
「送信するものは厳密に、受信するものは寛容に」という原則です。サーバーは柔軟にリクエストを受け入れ、クライアントは柔軟にレスポンスを処理します。

バージョニングの主要方式を比較する

APIバージョニングには主に3つの方式があります。それぞれの特徴を詳しく見ていきましょう。

URLパスバージョニング

最も広く使われている方式で、URLのパスにバージョン番号を含めます。

# URLパスバージョニングの例
GET https://api.example.com/v1/users
GET https://api.example.com/v2/users
POST https://api.example.com/v1/users
POST https://api.example.com/v2/users

メリット

  • 最も直感的で理解しやすい
  • URLを見るだけでバージョンがわかる
  • ブラウザのアドレスバーやcurlコマンドでの確認が容易
  • APIゲートウェイでのルーティングが簡単
  • キャッシュが容易(URLが異なるため)

デメリット

  • RESTの原則(URIはリソースを識別する)に厳密には違反する
  • バージョンが変わるとURLが変わるため、すべてのクライアントがURLを更新する必要がある
  • バージョンごとにルーティング設定が必要

採用企業:GitHub API、Google Maps API、Twitter API、Stripe APIなど

HTTPヘッダーバージョニング

カスタムHTTPヘッダーでバージョンを指定する方式です。

# HTTPヘッダーバージョニングの例
GET https://api.example.com/users
Accept: application/vnd.example.v2+json

# または独自ヘッダー
GET https://api.example.com/users
X-API-Version: 2

メリット

  • URLがクリーンに保たれる(リソースの識別に専念)
  • RESTの原則に沿っている
  • コンテントネゴシエーションの仕組みを活用できる

デメリット

  • ブラウザのアドレスバーやリンクだけではバージョンがわからない
  • curlコマンドでの動作確認に毎回ヘッダー指定が必要
  • HTTPキャッシュの設定が複雑になる(Varyヘッダーの設定が必要)
  • APIゲートウェイの設定が複雑になる場合がある

採用企業:GitHub API(Accept ヘッダーも併用)

クエリパラメータバージョニング

クエリパラメータでバージョンを指定する方式です。

# クエリパラメータバージョニングの例
GET https://api.example.com/users?version=2
GET https://api.example.com/users?api-version=2026-03-01

メリット

  • バージョン省略時にデフォルトバージョンを適用できる
  • 既存のURLを変更せずにバージョニングを導入できる
  • テスト時にパラメータを変えるだけで異なるバージョンを試せる

デメリット

  • クエリパラメータが長くなり、URLが煩雑になる
  • バージョンパラメータがビジネスロジックのパラメータと混在する
  • URLパスバージョニングほど一般的ではない

採用企業:Amazon Web Services API、Microsoft Azure API(日付ベース)

方式の選定ガイドライン

迷ったらURLパスバージョニングを選ぶのが無難です。理由は以下のとおりです。

  • 業界で最も広く採用されており、開発者にとって馴染みがある
  • 実装とテストが容易
  • APIゲートウェイやCDNとの相性が良い
  • デバッグが容易(URLを見るだけでバージョンがわかる)

バージョン番号の設計

バージョン番号の付け方にもいくつかの方式があります。

メジャーバージョンのみ

最もシンプルで、破壊的変更がある場合にのみバージョンを上げます。

# v1, v2, v3 のように管理
/v1/users  → 初期バージョン
/v2/users  → 破壊的変更を含む新バージョン

非破壊的変更は既存バージョン内で追加するため、バージョン番号が頻繁に変わることはありません。多くのAPIではメジャーバージョンがv1からv3程度に留まるのが理想です。バージョン番号が大きくなるほど、クライアントの移行負担が増大します。

日付ベースのバージョニング

Stripeが採用している方式で、APIバージョンを日付で管理します。

# Stripeのバージョニング例
Stripe-Version: 2026-03-01

# curlでの指定
curl https://api.stripe.com/v1/charges \
  -H "Stripe-Version: 2026-03-01" \
  -H "Authorization: Bearer sk_test_..."

この方式では、各クライアントが登録時のAPIバージョンに固定され、明示的にバージョンを更新しない限り動作が変わりません。日付で管理することで、変更の時系列が把握しやすくなります。

セマンティックバージョニングとの関係

セマンティックバージョニング(SemVer: MAJOR.MINOR.PATCH)はライブラリのバージョニングには適していますが、APIのURLバージョニングにはメジャーバージョンのみを使うのが一般的です。マイナーバージョンやパッチバージョンはAPI内部で管理し、クライアントには見せません。

バージョン共存のアーキテクチャ設計

複数のAPIバージョンを同時に運用するためのアーキテクチャパターンを紹介します。

ルーティング層でのバージョン分岐

// Express.jsでのルーティングによるバージョン管理
import express from 'express';
import v1Router from './routes/v1';
import v2Router from './routes/v2';

const app = express();

// バージョンごとにルーターを分離
app.use('/v1', v1Router);
app.use('/v2', v2Router);

// デフォルトバージョンへのリダイレクト
app.use('/users', (req, res) => {
  res.redirect(301, `/v2${req.url}`);
});
// routes/v1/users.ts
import { Router } from 'express';
const router = Router();

router.get('/users', async (req, res) => {
  const users = await userService.getAll();
  // v1のレスポンス形式
  res.json(users.map(u => ({
    id: u.id,
    name: u.name,
    email: u.email,
  })));
});

export default router;
// routes/v2/users.ts
import { Router } from 'express';
const router = Router();

router.get('/users', async (req, res) => {
  const users = await userService.getAll();
  // v2のレスポンス形式(ページネーション追加、フィールド変更)
  res.json({
    data: users.map(u => ({
      id: u.id,
      fullName: u.name,       // name → fullName に変更
      emailAddress: u.email,  // email → emailAddress に変更
      role: u.role,           // 新フィールド追加
      createdAt: u.createdAt,
    })),
    pagination: {
      page: 1,
      totalPages: 10,
      totalCount: 195,
    },
  });
});

export default router;

コントローラー層でのバージョン分岐

共通のビジネスロジック(サービス層)を共有し、レスポンスの変換のみをバージョンごとに分けるパターンです。

// アダプターパターンでバージョン差分を吸収
interface UserResponseAdapter {
  transform(user: UserEntity): object;
}

class UserResponseV1 implements UserResponseAdapter {
  transform(user: UserEntity) {
    return {
      id: user.id,
      name: user.name,
      email: user.email,
    };
  }
}

class UserResponseV2 implements UserResponseAdapter {
  transform(user: UserEntity) {
    return {
      id: user.id,
      fullName: user.name,
      emailAddress: user.email,
      role: user.role,
      createdAt: user.createdAt,
    };
  }
}

// バージョンに応じてアダプターを選択
function getAdapter(version: string): UserResponseAdapter {
  switch (version) {
    case 'v1': return new UserResponseV1();
    case 'v2': return new UserResponseV2();
    default: return new UserResponseV2();
  }
}

このアプローチでは、ビジネスロジックの重複を避けつつ、バージョンごとのレスポンス差分を明確に管理できます。

APIゲートウェイでのバージョンルーティング

# Nginx でのバージョンルーティング
upstream api_v1 {
    server v1-service:3000;
}

upstream api_v2 {
    server v2-service:3000;
}

server {
    location /v1/ {
        proxy_pass http://api_v1/;
    }

    location /v2/ {
        proxy_pass http://api_v2/;
    }

    # デフォルトは最新バージョンへ
    location /api/ {
        proxy_pass http://api_v2/;
    }
}

非推奨化と廃止のプロセス

古いバージョンを安全に廃止するためのプロセス設計は、バージョニング戦略の重要な一部です。

非推奨化(Deprecation)のステップ

ステップ1:告知期間(3〜6ヶ月前)
ドキュメント、API changelog、メール通知でバージョン廃止の予定を告知します。

# 非推奨ヘッダーの付与
HTTP/1.1 200 OK
Deprecation: Sun, 27 Sep 2026 00:00:00 GMT
Sunset: Mon, 27 Mar 2027 00:00:00 GMT
Link: <https://api.example.com/v2/users>; rel="successor-version"

ステップ2:移行支援期間
移行ガイドを提供し、v1からv2への変更点と対応方法を文書化します。

# 移行ガイドの例(CHANGELOG.md)
## v2への移行ガイド

### 破壊的変更
- `GET /v1/users` のレスポンス形式が変更されました
  - `name` → `fullName` に名称変更
  - `email` → `emailAddress` に名称変更
  - `role` フィールドが追加されました
  - レスポンスが `{ data: [], pagination: {} }` 形式に変更

### 移行手順
1. クライアントコードの `name` を `fullName` に変更
2. クライアントコードの `email` を `emailAddress` に変更
3. ページネーション対応のため、レスポンス処理を `response.data` に変更

ステップ3:利用状況の監視
廃止予定のバージョンのリクエスト数を監視し、移行が進んでいないクライアントに個別に連絡します。

// 非推奨バージョンの利用状況を記録するミドルウェア
function deprecationTracker(req, res, next) {
  const apiKey = req.headers['x-api-key'];
  const version = req.baseUrl.match(/\/v(\d+)/)?.[1];

  if (version === '1') {
    // 利用状況をログに記録
    logger.warn('非推奨APIバージョンの利用', {
      apiKey,
      version,
      endpoint: req.path,
      ip: req.ip,
    });

    // 非推奨ヘッダーを付与
    res.set('Deprecation', 'true');
    res.set('Sunset', 'Mon, 27 Mar 2027 00:00:00 GMT');
  }

  next();
}

ステップ4:廃止(Sunset)
告知期間を経て、旧バージョンを廃止します。即座に404を返すのではなく、まずは410 Gone で「このバージョンは廃止された」ことを明示するレスポンスを返します。

// 廃止済みバージョンのハンドリング
app.use('/v1', (req, res) => {
  res.status(410).json({
    error: 'API_VERSION_SUNSET',
    message: 'v1 APIは2027年3月27日をもって廃止されました。v2をご利用ください。',
    migrationGuide: 'https://docs.example.com/migration/v1-to-v2',
    currentVersion: 'https://api.example.com/v2',
  });
});

バージョニング戦略のベストプラクティス

最後に、APIバージョニングの全体的なベストプラクティスをまとめます。

設計段階のベストプラクティス

バージョン変更を最小限にする設計
最初のAPI設計時に拡張性を考慮し、破壊的変更の必要性を減らします。レスポンスフィールドは追加可能な構造にしておきます。

同時に運用するバージョンは2つまで
3つ以上のバージョンの同時運用は、保守コストが急激に増大します。新バージョンをリリースしたら、最も古いバージョンの廃止を計画します。

変更ログの維持
すべてのAPI変更を日付付きで記録します。変更ログは、クライアント開発者が移行計画を立てるための重要な情報源です。

運用段階のベストプラクティス

十分な移行期間の確保
最低3ヶ月、理想的には6ヶ月以上の移行期間を設けます。エンタープライズ向けAPIではさらに長い期間が必要な場合があります。

バージョン別のメトリクス監視
各バージョンのリクエスト数、エラー率、レイテンシを監視し、移行の進捗とサービス品質を把握します。

APIバージョニングポリシーの明文化
バージョニングのルール(いつ新バージョンを作るか、非推奨化の期間はどれくらいか)をポリシーとして明文化し、クライアント開発者に公開します。

まとめ

APIバージョニングは、長期的なAPI運用の安定性を確保するための重要な設計判断です。本記事のポイントを振り返りましょう。

まずは避ける努力を
拡張可能な設計とTolerant Readerパターンにより、破壊的変更の必要性自体を減らすことが最善策です。

方式の選択
迷ったらURLパスバージョニング(/v1/users)を選びましょう。業界で最も広く採用されており、実装・テスト・デバッグが容易です。

バージョンは少なく
同時運用するバージョンは2つまでに抑え、メジャーバージョンの数もできるだけ少なくする設計を心がけましょう。

廃止プロセスの設計
告知、移行支援、監視、廃止という段階的なプロセスを事前に設計しておくことで、スムーズなバージョン移行を実現できます。

バージョニング戦略は一度決めると変更が困難です。API設計の初期段階でチーム内で方針を合意し、ドキュメント化しておくことを強くおすすめします。

#API#バージョニング#設計
ManageAI 先行モニター企業 募集中|先着5社・2026年11月末まで

生成AIの利用費、「誰が・何のために・いくら」まで見えていますか?

株式会社カイダンと共同開発したAI利用ガバナンスSaaS「ManageAI」は、OpenAI・Azure OpenAI・Claude・AWS Bedrock・Google Vertex AIの費用と利用状況を、部署・利用者・プロジェクト別に可視化します。導入は読み取り専用キーの登録だけ。初期費用0円・月額5万円(税別)で先行モニター企業を募集しています。

ニュースリリースを読む
共有:
無料メルマガ

週1回、最新の技術記事をお届け

AI・クラウド・開発の最新記事を毎週月曜にメールでお届けします。登録は無料、いつでも解除できます。

※ プライバシーポリシーに基づき管理します

関連記事

Related / 9 articles

  1. プログラミング

    DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用

    DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。

  2. プログラミング

    WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド

    WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。

  3. プログラミング

    セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説

    CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。

  4. プログラミング

    クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計

    クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。

  5. プログラミング

    秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除

    APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。

  6. プログラミング

    開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御

    開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。

  7. プログラミング

    ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方

    NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。

  8. プログラミング

    ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方

    ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。

  9. プログラミング

    localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決

    localhost・127.0.0.1・0.0.0.0の意味の違い、Dockerで外から接続できない原因、host.docker.internal、ポート競合の調べ方、Unixソケットの利点と権限、1024未満ポートの制約を解説。「つながらない」を仕組みから解決できます。

ManageAI

先行モニター企業 募集中

生成AIのコストを「部署別」に、請求書より先に。

先着5社限定・2026年11月末まで。初期費用0円・月額5万円(税別)で、正式版と同等の機能に加え、開発者と伴走者による導入支援を追加費用なしで提供します。プロンプト原文は一切取得しません。

株式会社カイダン × Harmonic Society株式会社の共同事業|読み取り専用キーの登録だけで導入できます