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

kento_morota 17分で読めます

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#バージョニング#設計
共有:
無料メルマガ

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

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

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

AI活用のヒントをお探しですか?お気軽にご相談ください。

まずは話だけ聞いてもらう