REST APIの命名規則:一貫性のあるエンドポイントを設計する10のルール
2年以上前のコードベースを開けば、そこに傷跡を見つけるでしょう。/getUser、/user_list、[REDACTED PATH]IDフィールドがorder_idの隣に存在している、といった具合です。どれも単独では何かを壊しません。しかし、積み重なるとチーム全体の作業を遅らせます。
命名は、API設計で最も安価に決められる一方、後から変更するには最も高価な要素です。クライアントが/getOrdersに依存すれば、その名前を何年もサポートすることになります。
この記事では、REST APIの命名に関する具体的なルールを、推奨例と非推奨例とともに紹介します。開発者向けREST APIガイドラインと同じ考え方に基づきつつ、チームで議論になりやすい「物事をどう呼ぶか」に焦点を当てます。
コードレビューだけに頼らず、ツールでルールを強制したい場合は、Apidogで共有スキーマに対してエンドポイントを視覚的に定義できます。
1. コレクションには複数形の名詞を使用する
URLは操作ではなく、リソースを表します。コレクションは複数のリソースの集合なので、複数形の名詞で命名します。
推奨:
GET /v1/products
GET /v1/products/89
GET /v1/orders
非推奨:
GET /v1/getProducts
GET /v1/product
GET /v1/productList
複数形なら、次の2つを自然に表現できます。
-
/products:製品のコレクション -
/products/89:コレクション内の製品89
単数形を使うと、1件のリソースには/product/89、複数のリソースには/productという不自然な構造になりがちです。Microsoft REST APIガイドラインもこの理由から複数形を採用しており、Stripe、GitHub、Shopifyなど多くの公開APIも同じ方式です。
ただし、シングルトンリソースは例外です。ユーザーがカートを1つだけ持つなら、`[REDACTED PATH]数が1のリソースには単数形を使用します。
2. パスから動詞を除外する
HTTPメソッドが動詞の役割を担います。パスにも動詞を含めると情報が重複し、リソースモデルが不明確になります。
推奨:
http
GET /v1/orders/42 (読み取り)
DELETE /v1/orders/42 (削除)
PATCH /v1/orders/42 (更新)
非推奨:
http
GET /v1/fetchOrder/42
POST /v1/deleteOrder/42
POST /v1/updateOrderStatus
動詞ベースのパスは、APIの表面積も増やします。1つのリソースに4つのメソッドがあれば、それぞれを個別にドキュメント化、テスト、キャッシュする必要があります。
キャッシュの無効化も複雑になります。CDNはGET /v1/orders/42をキャッシュし、DELETE /v1/orders/42で無効化できます。どちらも同じURLを指すためです。一方、/fetchOrder/42と/deleteOrder/42を同じリソースとして関連付けるのは困難です。
3. URLパスにはケバブケースを使用する
複数の単語で構成されるパスセグメントには、ハイフンを使います。
推奨:
http
/v1/gift-cards
/v1/shipping-addresses
非推奨:
http
/v1/giftCards
/v1/gift_cards
/v1/GiftCards
ケバブケースには、主に3つの利点があります。
- Googleはハイフンを単語の区切りとして扱うため、公開APIドキュメントの検索性を高められる
- URLに下線が引かれた場合でも、アンダースコアのように区切りが見えなくならない
- キャメルケースによる大文字・小文字の違いに起因するバグを避けられる
多くのサーバーでは/giftCardsと/giftcardsは異なるURLです。入力ミスによる問題も起こり得ます。Zalando RESTful APIガイドラインでも、ケバブケースはMUSTルールです。
4. JSONの命名規則を一つ選び、文書化する
JSONフィールド名は、キャメルケースでもスネークケースでも構いません。問題なのは、両者を混在させることです。
推奨:どちらか一方に統一する
json
{ "orderId": 42, "createdAt": "2026-08-30T09:15:00Z", "totalAmount": 4999 }
json
{ "order_id": 42, "created_at": "2026-08-30T09:15:00Z", "total_amount": 4999 }
非推奨:
json
{ "orderId": 42, "created_at": "2026-08-30T09:15:00Z", "TotalAmount": 4999 }
キャメルケースはJavaScriptやJavaクライアントにマッピングしやすく、スネークケースは読みやすく、Ruby、Python、SQLのカラム名と対応させやすいという特徴があります。Stripeはスネークケースを全面的に採用しています。
APIの主要な利用者に合わせて一方を選び、スタイルガイドに明記しましょう。エンドポイントごとに異なる命名規則が使われるのは、好みの問題ではなくガバナンスの失敗です。
5. ネストは2レベルまでにする
ネストは所有関係を表します。たとえば、`[REDACTED PATH]
推奨:
GET /v1[REDACTED PATH]
GET /v1/orders/1337/refunds
非推奨:
GET /v1[REDACTED PATH]
3レベル以上の深いネストは、リーフリソースがグローバルに一意なIDを持っていても、クライアントにすべての祖先IDを要求します。
払い戻しがID 7を持つなら、次のどちらかで十分です。
/refunds/7
/orders/1337/refunds/7
URLに3つ以上のIDが含まれていたら、平坦化を検討してください。注文が存在すれば、そのパスにユーザーIDを含める必要はありません。/orders/1337だけで独立したリソースとして扱えます。
6. フィルタリング、ソート、ページネーションはクエリパラメータに入れる
パスはリソースを識別し、クエリパラメータは取得方法を指定します。フィルターをパスに埋め込まないようにします。
推奨:
GET /v1/orders?status=active&sort=-created_at&limit=50&cursor=eyJpZCI6NDJ9
GET /v1/products?category=electronics&min_price=1000
非推奨:
GET /v1/orders/active
GET /v1/orders/sorted-by-date-desc
GET /v1/getOrdersByStatusAndDate
sort=-created_atのように、マイナス記号で降順を表す形式はJSON:API仕様に由来します。order=descのような追加パラメータを不要にできるのが利点です。
/orders/activeのようなパスは一見便利ですが、複数のフィルターを組み合わせるたびに新しいエンドポイントが必要になります。
ページネーションのパラメータも統一してください。
-
limit/cursor -
page/per_page
どちらかを選び、すべてのコレクションで再利用します。APIページネーションガイドでは、カーソル方式とオフセット方式のトレードオフを詳しく解説しています。
7. パスにメジャーバージョンを記述する
APIバージョニングには、主に次の2つの方式があります。
- パスセグメント:
/v1/products - ヘッダー:
Accept: application/vnd.myapi.v1+json
ヘッダーによるバージョニングは、バージョン間で同じURLが同じリソースを指し続けるため、より「純粋な」RESTと考えられます。Google API設計ガイドも、両方のアプローチが広く使われていると説明しています。
一方、パスバージョニングには運用上の利点があります。
- すべてのログでバージョンを確認できる
- ブラウザから直接テストできる
-
Varyを複雑に扱わずキャッシュできる - クライアントがバージョン指定を忘れにくい
バージョンヘッダーの欠落による「curlでは動くが、本番では失敗する」という問題は、デバッグを難しくします。
メジャーバージョンのみをパスに含め、/v1/を使用してください。/v1.2/のようなマイナーバージョンは避け、マイナーな変更は後方互換性のある追加としてリリースします。コンテンツネゴシエーションを含む詳しい比較は、APIバージョン管理戦略を参照してください。
8. リソースIDを不透明に扱う
次のような連番IDは、処理件数を推測させ、IDの列挙を容易にします。
/orders/41
/orders/42
/orders/43
攻撃者がIDを順番に試し、認証の隙間を探す可能性があります。不適切なオブジェクトレベル認証は、OWASP API Security Top 10で第1位に挙げられています。
推奨:
GET /v1/orders/ord_9f8e2a71b3
GET /v1/users/550e8400-e29b-41d4-a716-446655440000
非推奨:列挙が問題になる場合
GET /v1/orders/42
GET /v1/invoices/10883
Stripeのord_9f8e2a71b3のような、接頭辞付きのランダムIDは有効なパターンです。
- 推測しにくい
- ログでリソース種別を判別しやすい
- 外部に安全に公開しやすい
ただし、不透明なIDは認証チェックの代わりにはなりません。認証・認可は必ず実装してください。内部では整数の主キーを使っても問題ありません。このルールは、URLで外部に公開するIDに関するものです。
9. 非CRUDアクションをコントローラーリソースとしてモデル化する
注文のキャンセル、支払いの再試行、メールの再送信など、通常のCRUDでは表現しにくい操作は必ず発生します。
推奨:
POST /v1/orders/42/cancel
POST /v1/payments/pay_88a1/retry
非推奨:
PATCH /v1/orders/42 { "status": "cancelled" }
POST /v1/cancelOrder { "orderId": 42 }
これは、動詞を使わないルールに対する限定的な例外です。動詞はパスの末尾に置き、対象リソースの下にスコープします。
PATCHでステータスだけを変更すると、状態マシンがフィールド更新の中に隠れてしまいます。注文のキャンセルによって、払い戻し、在庫の解放、通知の送信が発生するかもしれません。単なるフィールド書き込みに見せると、サーバーはペイロードを比較して意図を推測する必要があります。
/cancelのようなアクションエンドポイントなら、次のことを明確にできます。
- 操作の意図
- アクション固有の権限
- 独立した監査証跡
- キャンセル理由などの専用入力
10. ヘッダーとクエリパラメータの命名規則を統一する
カスタムヘッダーには、HTTPの慣習に合わせてハイフン区切りのパスカルケースを使用します。
Idempotency-Key
Request-Id
古いX-プレフィックスは使用しないでください。RFC 6648によって2012年に非推奨となっています。
ヘッダー名は通信上では大文字・小文字を区別しませんが、ドキュメントやSDKでは常に同じ表記を使います。
クエリパラメータは、JSONボディの命名規則に合わせます。ボディがスネークケースなら、クエリもスネークケースにします。
?min_price=1000&created_after=2026-01-01
?minPrice=1000のように別の規則を使うと、レスポンスではcreated_at、クエリではcreatedAfterという不統一が生まれます。最初の利用者が間違えれば、その後の利用者も同じ間違いをするでしょう。
ルールセット全体を一目で確認
| # | ルール | 推奨 | 非推奨 |
|---|---|---|---|
| 1 | コレクションには複数形の名詞を使用する |
/products, /products/89
|
/getProducts, /productList
|
| 2 | パスに動詞を含めない | DELETE /orders/42 |
POST /deleteOrder/42 |
| 3 | パスセグメントにはケバブケースを使用する | /gift-cards |
/giftCards, /gift_cards
|
| 4 | JSONの命名規則は一つに統一し、文書化する |
order_id をどこでも使用 |
orderId と order_id を混在させる |
| 5 | ネストは最大2レベルまで | /orders/1337/refunds |
/users/42/orders/1337/refunds/7 |
| 6 | フィルターとページネーションはクエリパラメータに入れる | ?status=active&sort=-created_at |
/orders/active |
| 7 | パスにメジャーバージョンを含める | /v1/products |
/v1.2/products, バージョンヘッダー |
| 8 | リソースIDは不透明にする | /orders/ord_9f8e2a71b3 |
/orders/42(公開、列挙可能) |
| 9 | アクションにはコントローラーパターンを使用する | POST /orders/42/cancel |
PATCHで {"status":"cancelled"}
|
| 10 | ヘッダーとパラメータの命名規則を統一する |
Idempotency-Key, ?min_price=
|
X-IDEMPOTENCY_KEY, ?minPrice= を混在させる |
大規模なコンベンションを適用する
Wikiにスタイルガイドを置くだけでは、APIの一貫性は保てません。一貫したAPIを運用するチームは、コードを書く前に設計し、その段階でコンベンションを適用します。これが実践的なAPIガバナンスの中心です。
Apidogでは、エンドポイントをスキーマファーストのビジュアルデザイナーで定義できます。パス、キャメルケース、パラメータ名が、コントローラーコード内の文字列ではなく、明示的な設計成果物になります。
共有コンポーネントを使えば、Pagination、Error、Moneyなどのスキーマを一度定義し、すべてのエンドポイントで再利用できます。新しいサービスごとにper_pageをpageSizeとして再発明する必要もありません。
設計はレビュー機能を備えたチームワークスペースで管理できます。そのため、3つのクライアントが統合した後ではなく、名前変更のコストが低い設計段階で/getUserOrdersのような問題を発見できます。
承認済みの仕様は、ドキュメント、モックサーバー、テストを駆動します。結果として、設計で承認された名前が、そのままチーム全体が出荷する名前になります。
Apidogをダウンロードして、次の新しいエンドポイントで無料で試してみてください。既存APIの改修は難しくても、新しいAPIで一貫性を保つことは難しくありません。
よくある質問
RESTのURLは複数形と単数形のどちらにすべきですか?
複数のインスタンスを持つリソース(/products、/orders、/users)には複数形を使います。複数形なら、コレクション(/orders)と個々のメンバー(/orders/42)の両方を自然に表現できます。
`[REDACTED PATH]を使用します。リソースモデリングの詳しい理由は、REST APIとは何かで基本原理から解説しています。
JSONフィールド名にはcamelCaseとsnake_caseのどちらが優れていますか?
どちらにも利点があります。
- camelCase:JavaScriptを多用するユーザーに適している
- snake_case:読みやすく、Python、Ruby、Stripeの公開APIと一致する
重要なのは、どちらか一方を選び、スタイルガイドに記載し、スキーマレビューで強制することです。エンドポイント間で命名規則が混在する方が、どちらを選ぶかより大きな問題になります。
APIバージョンはURLに入れるべきですか、それともヘッダーに入れるべきですか?
強力なハイパーメディア要件がない限り、パス(/v1/orders)を推奨します。パスバージョンは、クライアントが追加の設定をしなくても、ログ、キャッシュ、ブラウザテストに表示されます。
ヘッダーによるバージョニングはURLを安定させられますが、クライアントがヘッダーを忘れるとサイレントに失敗する可能性があります。メジャーバージョンだけを使用し、マイナーな変更は後方互換性のある追加としてリリースしてください。
REST APIパスで動詞が許容されることはありますか?
はい。非CRUDアクション用のコントローラーエンドポイントでは許容されます。
http
POST /orders/42/cancel
POST /payments/pay_88a1/retry
動詞はパスの最後に置き、対象リソースのスコープ内に配置します。メソッドはPOSTを使用します。
それ以外では、HTTPメソッドが動詞を伝え、パスは名詞だけで構成します。
Top comments (0)