要点
REST APIのリソース名は複数形にすべきです。/pet/{id}ではなく、/pets/{id}を使用してください。複数形名は一貫してコレクションを表し、HTTPのセマンティクスに沿っており、開発者がリソースについて考える方法と一致します。Modern PetstoreAPIは、業界のベストプラクティスに従い、API設計全体で複数形名を使用しています。
はじめに
あなたはREST APIを設計しています。IDでユーザーを取得するためのエンドポイントが必要だとします。その際、/user/123と/users/123のどちらを使いますか?この疑問は、数えきれないほどの議論、Stack Overflowのスレッド、チーム内の口論を引き起こしてきました。
答えは明確です。複数形を使用してください。しかし、ルールを覚えることよりも、その理由を理解することの方が重要です。その理由は、RESTの仕組み、コレクションの振る舞い、そして開発者がリソースについてどのように考えるかに繋がっています。
古いSwagger Petstoreは、/pets/{id}ではなく/pet/{id}を使用してこの点を誤っていました。この一貫性のなさは、何百万もの開発者に誤ったパターンを教えてしまいました。Modern PetstoreAPIは、すべてのエンドポイントで複数形名を一貫して使用することで、この問題を解決しています。
このガイドでは、なぜ複数形名が正しい選択なのか、それがRESTの原則とどのように合致するのか、そしてModern PetstoreAPIを参照として使用してそれらを正しく実装する方法について学びます。
複数形と単数形の議論
この議論が存在するのは、どちらのアプローチも一見すると論理的に見えるためです。
単数形の主張
「/user/123をリクエストするとき、私は複数のユーザーではなく、1人のユーザーを取得しています。単数形は理にかなっています。」
この推論は、レスポンスに焦点を当てています。つまり、単一のリソースを取得しているので、URLも単数形であるべきだというものです。
複数形の主張
「URLはコレクションを表します。/usersはすべてのユーザーのコレクションです。/users/123はそのコレクション内のアイテム123です。」
この推論は、リソースの構造に焦点を当てています。つまり、URLはコレクションを表し、そのコレクション内のアイテムにアクセスしているというものです。
なぜこれが重要なのか
あなたの選択は以下に影響します。
- APIの一貫性 - 命名が混在していると開発者を混乱させる
- メンタルモデル - 開発者がAPI構造をどのように理解するか
- コード生成 - ツールはリソース名に基づいてクライアントコードを生成する
- ドキュメントの明確さ - ドキュメントは命名ロジックを説明する必要がある
なぜ複数形名が優れているのか
複数形のリソース名は、RESTの原則とHTTPのセマンティクスに合致しています。その理由を以下に示します。
1. コレクションは複数形である
RESTでは、リソースはコレクションです。/usersにアクセスするとき、あなたはユーザーコレクションにアクセスしています。/users/123にアクセスするとき、あなたはユーザーコレクション内のアイテム123にアクセスしています。
GET /users ← ユーザーコレクション
GET /users/123 ← ユーザーコレクション内のアイテム123
POST /users ← ユーザーコレクションに追加
DELETE /users/123 ← ユーザーコレクションからアイテム123を削除
このメンタルモデルは一貫しています。すべてのアイテムにアクセスする場合でも、1つのアイテムにアクセスする場合でも、コレクションは常に/usersです。
単数形名では、メンタルモデルが崩れます。
GET /user ← どのユーザー?
GET /user/123 ← これは理にかなっている
POST /user ← 何に追加する?
2. HTTPメソッドはコレクションに対して操作する
HTTPメソッドはコレクションに対する操作を記述します。
GET /users- コレクションを取得するPOST /users- コレクションに追加するGET /users/123- コレクションからアイテム123を取得するPUT /users/123- コレクション内のアイテム123を置き換えるDELETE /users/123- コレクションからアイテム123を削除する
コレクションがリソースです。個々のアイテムはそのコレクションのメンバーです。
3. エンドポイント間の一貫性
複数形名は一貫性を生み出します。
GET /pets ← コレクション
GET /pets/123 ← コレクション内のアイテム
GET /orders ← コレクション
GET /orders/456 ← コレクション内のアイテム
単数形名では、単数形と複数形を切り替えることを強いられます。
GET /pet ← 意味をなさない
GET /pet/123 ← 理にかなっている
GET /pets ← 待てよ、今度は複数形か?
4. 業界標準
主要なAPIは複数形名を使用しています。
- GitHub API:
/repos,/users,/issues - Stripe API:
/customers,/charges,/subscriptions - Twilio API:
/accounts,/messages,/calls - Google APIs:
/users,/groups,/files
Modern PetstoreAPIは、/pets、/orders、/usersを使用してこの標準に従っています。
コレクションのメンタルモデル
コレクションを理解することは、より良いAPIを設計するのに役立ちます。
RESTにおけるコレクション
コレクションはリソースのセットです。ペットストアAPIでは:
/pets- すべてのペットのコレクション/orders- すべての注文のコレクション/users- すべてのユーザーのコレクション
各コレクションは以下の操作をサポートします。
GET /pets ← ペットを一覧表示(フィルタリング、ページネーション付き)
POST /pets ← 新しいペットを作成
GET /pets/{id} ← 特定のペットを取得
PUT /pets/{id} ← 特定のペットを更新
DELETE /pets/{id} ← 特定のペットを削除
サブコレクション
コレクションはサブコレクションを含むことができます。
GET /pets/{id}/photos ← ペット{id}の写真コレクション
POST /pets/{id}/photos ← ペット{id}に写真を追加
GET /pets/{id}/photos/{photoId} ← 特定の写真
このパターンは一貫しています。コレクションは複数形であり、アイテムはIDでアクセスされます。
Modern PetstoreAPIの例
Modern PetstoreAPIはこれを正しく実装しています。
GET /pets
GET /pets/{petId}
GET /pets/{petId}/photos
POST /pets/{petId}/vaccinations
GET /orders
GET /orders/{orderId}
GET /orders/{orderId}/items
すべてのコレクションは複数形です。すべてのアイテムアクセスは、そのコレクション内で{id}を使用します。
Modern PetstoreAPIが複数形名をどのように使用しているか
Modern PetstoreAPIからの実際の例を見てみましょう。
ペットリソース
GET /pets ← すべてのペットを一覧表示
POST /pets ← 新しいペットを作成
GET /pets/{petId} ← 特定のペットを取得
PUT /pets/{petId} ← ペットを更新
DELETE /pets/{petId} ← ペットを削除
GET /pets?status=AVAILABLE ← ステータスでペットをフィルタリング
注文リソース
GET /orders ← すべての注文を一覧表示
POST /orders ← 注文を作成
GET /orders/{orderId} ← 特定の注文を取得
PUT /orders/{orderId} ← 注文を更新
DELETE /orders/{orderId} ← 注文をキャンセル
ユーザーリソース
GET /users ← ユーザーを一覧表示
POST /users ← ユーザーを作成
GET /users/{userId} ← 特定のユーザーを取得
PUT /users/{userId} ← ユーザーを更新
DELETE /users/{userId} ← ユーザーを削除
ネストされたリソース
GET /pets/{petId}/photos ← ペットの写真
POST /pets/{petId}/photos ← 写真を追加
GET /pets/{petId}/vaccinations ← ペットの予防接種
POST /pets/{petId}/vaccinations ← 予防接種を記録
GET /orders/{orderId}/items ← 注文アイテム
完全なエンドポイントリストについては、REST APIの完全なドキュメントを確認してください。
単数形に対する一般的な主張(そしてなぜそれらが誤っているのか)
単数形名に対する一般的な主張に対処しましょう。
主張1:「レスポンスは単数形である」
主張:「/user/123をGETするとき、1人のユーザーを取得します。単数形は理にかなっています。」
反論:URLはリソースの場所を表し、レスポンスではありません。/users/123は「ユーザーコレクション内のアイテム123」を意味します。レスポンスが単数形であっても、コレクションの構造は変わりません。
主張2:「コード上では読みやすい」
主張:「getUser(id)はgetUsers(id)よりも読みやすい。」
反論:クライアントコードの命名はURL構造とは独立しています。次のように記述できます。
// URL: GET /users/123
function getUser(id) {
return api.get(`/users/${id}`);
}
関数名がURLと一致する必要はありません。
主張3:「単数形は文法上の問題を避ける」
主張:「『status』や『information』のように明確な複数形を持たないリソースについてはどうですか?」
反論:これらは通常、コレクションではなくシングルトンリソースです。シングルトンには単数形を使用します。
GET /status ← システムステータス(シングルトン)
GET /configuration ← アプリケーション設定(シングルトン)
GET /users ← ユーザーコレクション(複数形)
主張4:「私のORMは単数形のテーブル名を使用している」
主張:「私のデータベーステーブルは単数形(user, order)なので、APIもそれに合わせるべきです。」
反論:API設計はデータベースの実装詳細を漏らすべきではありません。REST APIはリソースを表すものであり、データベーステーブルではありません。データベーススキーマに関わらず、コレクションには複数形を使用してください。
Apidogを使ったリソース命名のテスト
Apidogは、リソースの命名規則を検証およびテストするのに役立ちます。
Modern PetstoreAPIをインポートする
- Modern PetstoreAPI OpenAPI仕様をインポートします
- Apidogはリソースパターンを自動的に検出します
- エンドポイントの命名の一貫性を確認します
命名規則テストを作成する
// Apidogテストスクリプト
pm.test("Resource names are plural", function() {
const path = pm.request.url.getPath();
const segments = path.split('/').filter(s => s);
// 最初のセグメントが複数形であることを確認
const resource = segments[0];
pm.expect(resource).to.match(/s$/); // 's'で終わる
});
一貫性を検証する
Apidogは以下をチェックできます。
- すべてのコレクションエンドポイントが複数形名を使用しているか
- サブリソースが同じパターンに従っているか
- 同じAPI内で単数形/複数形が混在していないか
実際のRequestでテストする
GET https://api.petstoreapi.com/v1/pets
GET https://api.petstoreapi.com/v1/pets/019b4132-70aa-764f-b315-e2803d882a24
GET https://api.petstoreapi.com/v1/orders
Apidogはレスポンスを検証し、API全体での命名の一貫性を確保するのに役立ちます。
エッジケースと例外
一部のリソースは複数形のパターンに当てはまりません。
シングルトンリソース
一度しか存在しないリソースは単数形であるべきです。
GET /status ← システムステータス
GET /configuration ← アプリケーション設定
GET /health ← ヘルスチェック
GET /metrics ← システムメトリクス
これらはコレクションではないため、複数形は適用されません。
コントローラーリソース
CRUD操作に当てはまらないアクション:
POST /login ← 認証アクション
POST /logout ← セッション終了
POST /search ← 複雑な検索操作
これらはリソースではなくアクションを表すため、許容される例外です。
不可算名詞
一部の名詞には明確な複数形がありません。
GET /information ← 単数形(不可算)
GET /data ← 単数形(不可算)
GET /equipment ← 単数形(不可算)
不可算名詞には単数形を使用しますが、これらは一般的なAPIでは稀です。
Modern PetstoreAPIのアプローチ
Modern PetstoreAPIはこれらのケースを処理します。
# コレクション(複数形)
GET /pets
GET /orders
GET /users
# シングルトン(単数形)
GET /health
GET /metrics
# アクション(単数動詞)
POST /login
POST /logout
結論
REST APIのリソース名は複数形にすべきです。これはコレクションのセマンティクス、HTTPメソッド、および業界標準に合致しています。Modern PetstoreAPIは、すべてのエンドポイントでこのパターンを正しく示しています。
主要なポイント:
- コレクションには複数形名を使用する(
/pets,/orders,/users) - 個々のアイテムはコレクション内でアクセスされる(
/pets/123) - シングルトンリソースは単数形にできる(
/status,/health) - 完璧さよりも一貫性が重要である
- Apidogで命名規則をテストする
複数形と単数形の議論は決着しました。業界標準に従い、複数形名を使用し、開発者が直感的に理解できるAPIを構築しましょう。
次のステップ:
- 命名の一貫性についてAPIエンドポイントを確認します
- 参照パターンについてはModern PetstoreAPIドキュメントを確認します
- Apidogを使用してAPI設計を検証します
- OpenAPI仕様を複数形のリソース名で更新します
よくある質問
既存のAPIを単数形から複数形に変更すべきですか?
APIがすでに本番環境で稼働している場合、リソース名の変更は破壊的変更になります。以下を考慮してください。
- 複数形名を持つ新しいv2エンドポイントを追加する
- リダイレクトで後方互換性を維持する
- 命名規則を明確に文書化する
命名の一貫性だけのために既存のクライアントを壊すべきではありません。
すでに複数形であるリソースについてはどうですか?
リソース名が元々複数形である場合(「analytics」や「series」など)、そのまま維持してください。
GET /analytics ← すでに複数形
GET /series ← すでに複数形
GET /species ← すでに複数形
ネストされたリソースはどのように処理しますか?
両方のレベルを複数形に保ちます。
GET /users/{userId}/orders ← 両方複数形
GET /pets/{petId}/vaccinations ← 両方複数形
GET /orders/{orderId}/items ← 両方複数形
チームが単数形を好む場合はどうなりますか?
API内の一貫性が最も重要です。チームがすでに単数形名で標準化しており、変更すると混乱を招く場合は、単数形を維持してください。しかし、新しいAPIには複数形を使用してください。
GraphQLは複数形と単数形のどちらを使用しますか?
GraphQLは通常、単一アイテムのクエリには単数形を、リストには複数形を使用します。
query {
user(id: "123") { ... } # 単数形
users(limit: 10) { ... } # 複数形
}
GraphQLクエリは単一か複数かを明示的に返すため、これはRESTとは異なります。
Modern PetstoreAPIはこれをどのように処理していますか?
Modern PetstoreAPIは、すべてのRESTエンドポイントで複数形名を一貫して使用しています。完全な例については、REST APIガイドを確認してください。
命名規則を自動的にテストできますか?
はい、Apidogは、API全体のリソース命名パターンをチェックするための自動テストを実行できます。OpenAPI仕様をインポートし、命名規則のテストケースを作成してください。
非英語のAPIについてはどうですか?
複数形のルールは言語に関係なく適用されます。APIがフランス語のリソース名を使用する場合は、フランス語の複数形を使用します。日本語を使用する場合は、日本語の文法規則に従います。原則(コレクションは複数形である)は変わりません。
