REST APIは、Webサービスやモバイルアプリケーションのバックエンドとして広く採用されているアーキテクチャスタイルです。しかし、「RESTful」という言葉を使いながらも、その本質的な設計原則を理解せずにAPIを設計しているケースは少なくありません。
本記事では、REST APIの設計原則を体系的に解説します。Roy Fieldingが提唱した6つのアーキテクチャ制約条件から、実際のエンドポイント設計のベストプラクティス、HTTPメソッドとリソースの対応関係、ステータスコードの適切な使い分け、そして避けるべきアンチパターンまで、API設計者が押さえるべき知識を網羅します。
前提条件と対象読者
本記事は以下の知識を持つ開発者を対象としています。
- HTTP通信の基礎(リクエスト/レスポンスの構造、ヘッダー情報)を理解している方
- バックエンドAPIの設計・実装経験がある、または学習中の方
- JSON形式のデータ交換について基本的な理解がある方
HTTP通信の基礎についてはHTTPリクエストとレスポンスの仕組みを図解で理解するで詳しく解説しています。
RESTとは何か
REST(Representational State Transfer)は、2000年にRoy Fieldingが博士論文で提唱したソフトウェアアーキテクチャスタイルです。WebのアーキテクチャをRESTfulに設計することで、スケーラビリティ、シンプルさ、変更容易性、可視性などの品質特性を実現できます。
RESTの誕生と背景
RESTは、HTTPプロトコルの設計にも関わったRoy Fieldingが、Web全体のアーキテクチャを分析・体系化する中で生まれました。Fieldingは、Webが大規模に成功している理由を「アーキテクチャ制約の組み合わせ」として説明し、それをRESTと名付けました。
flowchart TB
subgraph REST["REST(Representational State Transfer)"]
direction TB
A["クライアント・サーバー分離"]
B["ステートレス通信"]
C["キャッシュ可能"]
D["統一インターフェース"]
E["階層化システム"]
F["コードオンデマンド(オプション)"]
end
REST --> G["スケーラブルで<br>保守性の高いAPI"]RESTとRESTfulの違い
「REST」はアーキテクチャスタイルそのものを指し、「RESTful」はRESTの原則に従って設計されたシステムを形容する言葉です。つまり、RESTfulなAPIとは、RESTの制約条件を満たすように設計されたAPIを意味します。
| 用語 | 意味 |
|---|---|
| REST | アーキテクチャスタイルの名称 |
| RESTful | RESTの原則に従っている状態を表す形容詞 |
| REST API | RESTfulに設計されたWeb API |
RESTの6つのアーキテクチャ制約条件
REST APIを正しく設計するためには、Fieldingが定義した6つの制約条件を理解することが不可欠です。これらの制約を守ることで、スケーラブルで保守性の高いAPIを実現できます。
1. Client-Server(クライアント・サーバー分離)
クライアントとサーバーの関心事を分離することで、それぞれが独立して進化できるようにします。
flowchart LR
subgraph Client["クライアント"]
A["ユーザーインターフェース"]
B["ユーザー体験"]
end
subgraph Server["サーバー"]
C["データストレージ"]
D["ビジネスロジック"]
end
Client <-->|HTTP| Serverこの分離により得られるメリットは以下の通りです。
- クライアントはサーバーのデータストレージ実装を知る必要がない
- サーバーはクライアントのUI実装を知る必要がない
- 各コンポーネントを独立してスケール・更新できる
- フロントエンドとバックエンドで異なる技術スタックを採用可能
2. Stateless(ステートレス)
サーバーはクライアントの状態を保持しません。各リクエストには、そのリクエストを処理するために必要なすべての情報が含まれている必要があります。
sequenceDiagram
participant C as クライアント
participant S as サーバー
Note over C,S: ステートレス通信
C->>S: GET /users/123<br>Authorization: Bearer token123
S-->>C: 200 OK<br>ユーザー情報
Note over S: サーバーは状態を保持しない
C->>S: PUT /users/123<br>Authorization: Bearer token123<br>更新データ
S-->>C: 200 OK<br>更新後の情報ステートレス設計のメリットは以下の通りです。
- サーバーのスケーラビリティが向上(どのサーバーインスタンスでもリクエストを処理可能)
- 障害時のリカバリが容易
- リクエストの可視性が向上(各リクエストが自己完結)
ステートレス設計の実装ポイントとして、認証情報はリクエストヘッダー(Authorizationヘッダー等)で毎回送信します。セッション状態をサーバー側で保持するのではなく、JWT(JSON Web Token)などのトークンベースの認証を採用することで、ステートレス性を維持できます。
3. Cacheable(キャッシュ可能)
レスポンスは暗黙的または明示的に、キャッシュ可能かどうかを示す必要があります。キャッシュを適切に活用することで、ネットワーク効率とパフォーマンスが向上します。
|
|
キャッシュ制御に使用する主なヘッダーは以下の通りです。
| ヘッダー | 用途 |
|---|---|
| Cache-Control | キャッシュの動作を制御(max-age、no-cache、no-store等) |
| ETag | リソースのバージョンを識別するタグ |
| Last-Modified | リソースの最終更新日時 |
| Expires | キャッシュの有効期限(非推奨、Cache-Controlを優先) |
4. Uniform Interface(統一インターフェース)
RESTの最も特徴的な制約であり、クライアントとサーバー間の通信を標準化します。この制約は4つのサブ制約で構成されます。
4.1 リソースの識別
リソースはURIで一意に識別されます。リソースとは、操作の対象となるデータや概念を指します。
良い例:
GET /users/123 # ID 123のユーザーリソース
GET /products/456 # ID 456の商品リソース
GET /orders/789/items # ID 789の注文に含まれる商品一覧
4.2 表現によるリソース操作
クライアントは、リソースの「表現」を通じてリソースを操作します。同じリソースでも、リクエストヘッダーに応じてJSONやXMLなど異なる形式で表現できます。
|
|
4.3 自己記述的メッセージ
各メッセージには、そのメッセージを処理するために必要な情報がすべて含まれます。Content-Typeヘッダーでメディアタイプを指定し、どのように解釈すべきかを明示します。
4.4 HATEOAS(Hypermedia as the Engine of Application State)
レスポンスには、クライアントが次に実行可能なアクションへのリンク(ハイパーメディア)を含めます。
|
|
HATEOASを実装することで、APIの発見可能性が向上し、クライアントがAPIの構造をハードコードする必要がなくなります。
5. Layered System(階層化システム)
クライアントは、直接サーバーに接続しているのか、中間サーバー(ロードバランサー、キャッシュサーバー、ゲートウェイ等)を経由しているのかを意識する必要がありません。
flowchart LR
C["クライアント"] --> LB["ロードバランサー"]
LB --> Cache["キャッシュサーバー"]
Cache --> GW["APIゲートウェイ"]
GW --> S1["サーバー1"]
GW --> S2["サーバー2"]
GW --> S3["サーバー3"]階層化によるメリットは以下の通りです。
- セキュリティ強化(ファイアウォール、認証レイヤーの追加)
- パフォーマンス向上(キャッシュレイヤー、CDNの活用)
- スケーラビリティ(ロードバランシングによる負荷分散)
6. Code on Demand(オプション)
唯一のオプション制約です。サーバーからクライアントへ実行可能なコード(JavaScriptなど)を送信し、クライアントの機能を拡張できます。
|
|
この制約はオプションであり、すべてのREST APIで実装する必要はありません。Webアプリケーションでは一般的に使用されますが、モバイルアプリやマイクロサービス間通信では使用されないことが多いです。
リソース指向のエンドポイント設計
REST APIのエンドポイント設計では、「リソース」を中心に考えることが重要です。適切なエンドポイント設計は、APIの直感性と保守性を大きく左右します。
リソースとは何か
リソースとは、APIを通じて操作する対象となるエンティティや概念です。具体的には、ユーザー、商品、注文、記事など、ビジネスドメインにおける「もの」を指します。
flowchart TB
subgraph Resources["リソースの例"]
direction LR
A["users<br>ユーザー"]
B["products<br>商品"]
C["orders<br>注文"]
D["articles<br>記事"]
end
subgraph URI["URIでの表現"]
direction LR
E["/users"]
F["/products"]
G["/orders"]
H["/articles"]
end
A --> E
B --> F
C --> G
D --> Hエンドポイント設計のベストプラクティス
REST APIのエンドポイント設計には、広く受け入れられているベストプラクティスがあります。
1. 名詞を使用し、動詞を避ける
エンドポイントにはリソースを表す名詞を使用し、操作を表す動詞はHTTPメソッドで表現します。
良い例:
GET /users # ユーザー一覧を取得
POST /users # ユーザーを作成
GET /users/123 # 特定のユーザーを取得
悪い例:
GET /getUsers # 動詞がURIに含まれている
POST /createUser # 動詞がURIに含まれている
GET /getUserById/123
2. 複数形を使用する
コレクションを表すリソースには複数形を使用します。単数形と複数形が混在するとAPIの一貫性が損なわれます。
良い例:
/users
/products
/orders
悪い例:
/user # 単数形
/product
/order
3. 階層関係をパスで表現する
リソース間の関係性は、パス階層で表現します。
GET /users/123/orders # ユーザー123の注文一覧
GET /users/123/orders/456 # ユーザー123の注文456
GET /orders/456/items # 注文456の商品一覧
ただし、階層が深くなりすぎると可読性が低下します。一般的には2〜3階層までに留めることを推奨します。
4. クエリパラメータで絞り込み・ソート・ページングを行う
リソースのフィルタリング、ソート、ページングにはクエリパラメータを使用します。
GET /users?status=active # ステータスで絞り込み
GET /users?sort=created_at&order=desc # ソート
GET /users?page=2&limit=20 # ページング
GET /products?category=electronics&min_price=1000
リソース命名の規則
一貫性のあるリソース命名は、APIの使いやすさを向上させます。
| 規則 | 良い例 | 悪い例 |
|---|---|---|
| 小文字を使用 | /users | /Users |
| ハイフン区切り | /user-profiles | /user_profiles, /userProfiles |
| 複数形を使用 | /products | /product |
| 動詞を避ける | /users | /getUsers |
| ファイル拡張子を避ける | /users | /users.json |
HTTPメソッドとCRUD操作の対応
REST APIでは、HTTPメソッドを使用してリソースに対する操作を表現します。各メソッドには明確なセマンティクスがあり、適切に使い分けることが重要です。
HTTPメソッドの一覧と特性
| メソッド | 操作 | べき等性 | 安全性 | リクエストボディ |
|---|---|---|---|---|
| GET | リソースの取得 | あり | あり | なし |
| POST | リソースの作成 | なし | なし | あり |
| PUT | リソースの完全更新 | あり | なし | あり |
| PATCH | リソースの部分更新 | なし | なし | あり |
| DELETE | リソースの削除 | あり | なし | なし(通常) |
「べき等性」とは、同じリクエストを複数回実行しても結果が同じになる性質です。「安全性」とは、リソースの状態を変更しない性質を指します。
GETメソッドの使用例
リソースの取得に使用します。レスポンスはキャッシュ可能です。
|
|
|
|
POSTメソッドの使用例
新しいリソースの作成に使用します。サーバーがリソースのIDを生成する場合に適しています。
|
|
PUTメソッドの使用例
リソースの完全な更新(置換)に使用します。リクエストボディには更新後の完全なリソース表現を含めます。
|
|
PATCHメソッドの使用例
リソースの部分的な更新に使用します。変更したいフィールドのみをリクエストボディに含めます。
|
|
DELETEメソッドの使用例
リソースの削除に使用します。
|
|
PUTとPATCHの使い分け
PUTとPATCHの違いを正しく理解することは、REST API設計において重要です。
flowchart TB
subgraph Before["更新前のリソース"]
A["id: 1<br>name: 田中太郎<br>email: tanaka@example.com<br>age: 30"]
end
subgraph PUT["PUT /users/1"]
B["name: 田中太郎<br>email: new@example.com"]
end
subgraph PATCH["PATCH /users/1"]
C["email: new@example.com"]
end
subgraph PutResult["PUT後のリソース"]
D["id: 1<br>name: 田中太郎<br>email: new@example.com<br>age: null(消失)"]
end
subgraph PatchResult["PATCH後のリソース"]
E["id: 1<br>name: 田中太郎<br>email: new@example.com<br>age: 30(保持)"]
end
Before --> PUT --> PutResult
Before --> PATCH --> PatchResult| 観点 | PUT | PATCH |
|---|---|---|
| 更新範囲 | リソース全体を置換 | 指定したフィールドのみ更新 |
| 送信データ | 完全なリソース表現が必要 | 変更部分のみ |
| 未送信フィールド | null/デフォルト値になる | 既存値が保持される |
| べき等性 | あり | なし(実装依存) |
HTTPステータスコードの適切な使い分け
REST APIでは、HTTPステータスコードを使用してリクエストの処理結果を伝えます。適切なステータスコードを返すことで、クライアントは処理結果を正しく判断し、適切なエラーハンドリングを実装できます。
ステータスコードの分類
HTTPステータスコードは、最初の数字で5つのカテゴリに分類されます。
flowchart LR
subgraph Codes["HTTPステータスコード"]
direction TB
A["1xx: 情報レスポンス"]
B["2xx: 成功"]
C["3xx: リダイレクト"]
D["4xx: クライアントエラー"]
E["5xx: サーバーエラー"]
end2xx: 成功レスポンス
| コード | 名称 | 使用場面 |
|---|---|---|
| 200 | OK | GETリクエストの成功、PUT/PATCHによる更新成功 |
| 201 | Created | POSTによるリソース作成成功 |
| 202 | Accepted | 非同期処理の受付完了 |
| 204 | No Content | DELETEの成功(レスポンスボディなし) |
|
|
|
|
4xx: クライアントエラーレスポンス
クライアント側に問題がある場合に返します。
| コード | 名称 | 使用場面 |
|---|---|---|
| 400 | Bad Request | リクエストの構文エラー、バリデーションエラー |
| 401 | Unauthorized | 認証が必要、認証情報が無効 |
| 403 | Forbidden | 認証済みだがリソースへのアクセス権限がない |
| 404 | Not Found | リソースが存在しない |
| 405 | Method Not Allowed | 許可されていないHTTPメソッド |
| 409 | Conflict | リソースの状態との競合(楽観的ロック失敗等) |
| 422 | Unprocessable Entity | リクエストは正しいがセマンティックエラー |
| 429 | Too Many Requests | レート制限超過 |
|
|
|
|
|
|
5xx: サーバーエラーレスポンス
サーバー側に問題がある場合に返します。
| コード | 名称 | 使用場面 |
|---|---|---|
| 500 | Internal Server Error | サーバー内部エラー(予期しない例外等) |
| 502 | Bad Gateway | 上流サーバーからの無効なレスポンス |
| 503 | Service Unavailable | 一時的なサービス停止(メンテナンス等) |
| 504 | Gateway Timeout | 上流サーバーからのレスポンスタイムアウト |
|
|
401と403の使い分け
401と403は混同されやすいステータスコードですが、明確な違いがあります。
flowchart TB
A["リクエスト"] --> B{"認証情報はあるか?"}
B -->|なし| C["401 Unauthorized<br>認証が必要"]
B -->|あり| D{"認証情報は有効か?"}
D -->|無効| E["401 Unauthorized<br>認証情報が無効"]
D -->|有効| F{"権限はあるか?"}
F -->|なし| G["403 Forbidden<br>アクセス権限がない"]
F -->|あり| H["200 OK<br>リクエスト成功"]| 観点 | 401 Unauthorized | 403 Forbidden |
|---|---|---|
| 意味 | 認証が必要/認証失敗 | 認可されていない |
| 認証状態 | 未認証/認証エラー | 認証済み |
| 対処方法 | 正しい認証情報で再試行 | 権限を取得する |
API設計で避けるべきアンチパターン
REST APIの設計において、避けるべきアンチパターンを理解することは、良いAPIを設計するうえで重要です。
1. 動詞をURLに含める
悪い例:
POST /api/createUser
GET /api/getUsers
POST /api/deleteUser/123
POST /api/updateUserEmail
良い例:
POST /api/users
GET /api/users
DELETE /api/users/123
PATCH /api/users/123
HTTPメソッドが操作を表すため、URLに動詞を含める必要はありません。
2. 一貫性のないURL設計
悪い例:
GET /api/users
GET /api/product # 単数形と複数形の混在
GET /api/Order/123 # 大文字の使用
GET /api/user_profile # アンダースコアの使用
良い例:
GET /api/users
GET /api/products
GET /api/orders/123
GET /api/user-profiles
3. 適切でないステータスコードの使用
悪い例:
# すべてのエラーで200を返す
HTTP/1.1 200 OK
{
"success": false,
"error": "User not found"
}
# リソースが見つからない場合に500を返す
HTTP/1.1 500 Internal Server Error
{
"message": "User not found"
}
良い例:
# 適切なステータスコードを使用
HTTP/1.1 404 Not Found
{
"error": {
"code": "USER_NOT_FOUND",
"message": "指定されたユーザーが見つかりません"
}
}
4. バージョニングの欠如
悪い例:
GET /api/users # バージョンがない
良い例:
GET /api/v1/users # URLパスでバージョニング
GET /api/users # ヘッダーでバージョニング
X-API-Version: 1
APIのバージョニングには主に以下の方法があります。
| 方法 | 例 | メリット | デメリット |
|---|---|---|---|
| URLパス | /api/v1/users | 明確、キャッシュしやすい | URLが変わる |
| クエリパラメータ | /api/users?version=1 | 実装が容易 | キャッシュが複雑 |
| カスタムヘッダー | X-API-Version: 1 | URLがシンプル | 発見しにくい |
| Acceptヘッダー | Accept: application/vnd.api.v1+json | HTTP標準に準拠 | 複雑 |
5. 過度にネストしたURL
悪い例:
GET /api/companies/123/departments/456/employees/789/tasks/012/comments
良い例:
GET /api/tasks/012/comments
GET /api/comments?task_id=012
深いネストは可読性を損ない、URLが長くなりすぎます。2〜3階層に留め、必要に応じてクエリパラメータを使用します。
6. センシティブ情報のURL露出
悪い例:
GET /api/users?password=secret123
GET /api/login?token=abc123xyz
良い例:
POST /api/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "secret123"
}
URLはブラウザ履歴やサーバーログに記録されるため、パスワードやトークンなどのセンシティブ情報はリクエストボディやヘッダーで送信します。
7. 不十分なエラーレスポンス
悪い例:
HTTP/1.1 400 Bad Request
{
"error": "Invalid request"
}
良い例:
HTTP/1.1 400 Bad Request
{
"error": {
"code": "VALIDATION_ERROR",
"message": "入力内容に問題があります",
"details": [
{
"field": "email",
"code": "INVALID_FORMAT",
"message": "有効なメールアドレス形式ではありません"
}
],
"request_id": "req_abc123",
"documentation_url": "https://api.example.com/docs/errors#VALIDATION_ERROR"
}
}
クライアントが問題を特定し、対処できる十分な情報を提供することが重要です。
REST API設計チェックリスト
API設計時に確認すべきポイントをチェックリストとしてまとめます。
エンドポイント設計
- リソースは名詞で表現されているか
- 複数形で統一されているか
- 小文字とハイフン区切りで統一されているか
- URLに動詞が含まれていないか
- 階層が深すぎないか(2〜3階層以内)
HTTPメソッド
- GETはリソース取得のみに使用しているか
- POSTは新規作成に使用しているか
- PUT/PATCHは更新に適切に使い分けているか
- DELETEは削除のみに使用しているか
ステータスコード
- 成功時に適切な2xxコードを返しているか
- 作成成功時に201と Locationヘッダーを返しているか
- クライアントエラーに4xxを使用しているか
- サーバーエラーに5xxを使用しているか
- 401と403を正しく使い分けているか
レスポンス
- 一貫したレスポンス構造になっているか
- エラーレスポンスに十分な情報が含まれているか
- ページングはクエリパラメータで実装しているか
- 適切なContent-Typeが設定されているか
セキュリティとバージョニング
- APIバージョンが明示されているか
- センシティブ情報がURLに露出していないか
- 認証が必要なエンドポイントは保護されているか
まとめ
本記事では、REST APIの設計原則を体系的に解説しました。要点を整理します。
RESTの6つの制約条件(Client-Server、Stateless、Cacheable、Uniform Interface、Layered System、Code on Demand)を理解し、遵守することで、スケーラブルで保守性の高いAPIを設計できます。
エンドポイント設計では、リソース指向のアプローチを採用し、名詞・複数形・小文字を使用した一貫性のあるURL設計を心がけます。HTTPメソッドはCRUD操作に適切に対応させ、ステータスコードはリクエストの処理結果を正確に反映するものを選択します。
アンチパターンを避けることも同様に重要です。動詞のURL混入、一貫性のない設計、不適切なステータスコード、バージョニングの欠如などは、APIの品質を低下させる要因となります。
REST APIの設計は、単なる技術的な規約ではなく、APIを利用する開発者の体験を左右する重要な設計活動です。本記事で解説した原則とベストプラクティスを参考に、使いやすく保守性の高いAPIを設計してください。
参考リンク
- Fielding Dissertation: CHAPTER 5: Representational State Transfer (REST) - Roy Fieldingによる原典
- HTTP response status codes - MDN Web Docs - HTTPステータスコードの公式リファレンス
- HTTP request methods - MDN Web Docs - HTTPメソッドの公式リファレンス
- RFC 9110: HTTP Semantics - HTTP仕様の標準ドキュメント
- Microsoft REST API Guidelines - MicrosoftのREST APIガイドライン
- Google Cloud API Design Guide - GoogleのAPI設計ガイド