REST API設計ガイド
わかりやすく使いやすいAPIを作る
REST APIの設計原則から、URL設計、HTTPメソッドの使い分け、レスポンス形式、ページネーション、認証まで、良いAPI設計のベストプラクティスを解説します。
こんな人向けの記事です
- REST APIの設計原則を学びたい
- わかりやすいURL設計のルールを知りたい
- エラーレスポンスや認証の設計を理解したい
Step 1RESTとは何か
REST(Representational State Transfer)は、Web APIを設計するためのアーキテクチャスタイルです。HTTPプロトコルの仕組みを最大限活用し、シンプルで直感的なAPIを構築するための原則を定めています。
RESTの6つの原則
1. クライアント-サーバー分離:フロントエンドとバックエンドを独立させる2. ステートレス:各リクエストは独立し、サーバーはクライアントの状態を保持しない
3. キャッシュ可能:レスポンスにキャッシュの可否を明示する
4. 統一インターフェース:URLとHTTPメソッドで操作を表現する
5. 階層化システム:プロキシやロードバランサーを挟める設計にする
6. コードオンデマンド(任意):必要に応じてクライアントにコードを送る
RESTfulなAPIとそうでないAPIの違いを見てみましょう。
| 操作 | RESTfulでない設計 | RESTfulな設計 |
|---|---|---|
| ユーザー一覧取得 | GET /getUsers | GET /users |
| ユーザー作成 | POST /createUser | POST /users |
| ユーザー更新 | POST /updateUser?id=1 | PUT /users/1 |
| ユーザー削除 | GET /deleteUser?id=1 | DELETE /users/1 |
よくある間違い
URLに動詞を含めるのはRESTの原則に反します。URLは「リソース(名詞)」を表し、操作はHTTPメソッドで表現します。/getUsers ではなく GET /users が正しい設計です。
Step 2URL設計のベストプラクティス
URLはAPIの「住所」です。一目見て何のリソースを扱っているか分かるように設計します。
基本ルール
| ルール | 良い例 | 悪い例 |
|---|---|---|
| 名詞を使う(動詞は使わない) | /articles | /getArticles |
| 複数形を使う | /users | /user |
| 小文字を使う | /blog-posts | /BlogPosts |
| ハイフンで単語を区切る | /user-profiles | /user_profiles |
| ファイル拡張子を付けない | /users/1 | /users/1.json |
リソースの階層構造(ネスト)
リソース間に親子関係がある場合は、URLのパスで関係を表現します。
ネストしたURLの例
# ユーザー1の投稿一覧
GET /users/1/posts
# ユーザー1の投稿5番のコメント一覧
GET /users/1/posts/5/comments
# 投稿5番のコメント3番を取得
GET /users/1/posts/5/comments/3
ネストは2階層までに抑える
3階層以上のネストはURLが長くなりすぎて管理が難しくなります。深いネストが必要な場合は、フラットなURLにクエリパラメータを組み合わせる設計を検討しましょう。/users/1/posts/5/comments/3/likes よりも/comments/3/likes の方がシンプルです。
よく使うURLパターン
CRUDのURL設計
# コレクション(一覧)
GET /api/v1/articles # 記事一覧を取得
POST /api/v1/articles # 記事を新規作成
# 個別リソース
GET /api/v1/articles/42 # ID=42の記事を取得
PUT /api/v1/articles/42 # ID=42の記事を全体更新
PATCH /api/v1/articles/42 # ID=42の記事を部分更新
DELETE /api/v1/articles/42 # ID=42の記事を削除
# サブリソース
GET /api/v1/articles/42/comments # 記事42のコメント一覧
POST /api/v1/articles/42/comments # 記事42にコメントを追加
# 検索・フィルタリング
GET /api/v1/articles?category=tech&status=published
Step 3HTTPメソッドの正しい使い分け
HTTPメソッドはリソースに対する操作の種類を表します。正しく使い分けることで、URLを見ただけでAPIの挙動が予測できるようになります。
| メソッド | 操作 | 冪等性 | リクエストボディ | 成功時ステータス |
|---|---|---|---|---|
GET | 取得 | あり | なし | 200 OK |
POST | 作成 | なし | あり | 201 Created |
PUT | 全体更新 | あり | あり | 200 OK |
PATCH | 部分更新 | 条件付き | あり | 200 OK |
DELETE | 削除 | あり | なし | 204 No Content |
冪等性(べきとうせい)とは
同じリクエストを何度送っても、結果が同じになる性質のことです。GET:何度取得しても同じデータが返る(冪等)
DELETE:1回目で削除、2回目以降は404が返るが、サーバーの状態は変わらない(冪等)
POST:送るたびに新しいリソースが作成される(冪等でない)
PUT と PATCH の違い
PUT(全体更新 - すべてのフィールドを送る)
PUT /api/v1/users/1
Content-Type: application/json
{
"name": "山田太郎",
"email": "taro@example.com",
"age": 30,
"role": "admin"
}
PATCH(部分更新 - 変更するフィールドだけ送る)
PATCH /api/v1/users/1
Content-Type: application/json
{
"email": "new-taro@example.com"
}
PUTで一部のフィールドだけ送るとどうなる?
PUTは「リソース全体の置き換え」なので、送られなかったフィールドはnullやデフォルト値にリセットされる可能性があります。一部だけ更新したい場合は必ずPATCHを使いましょう。
Step 4レスポンス設計
APIのレスポンスはJSON形式が標準です。成功時もエラー時も一貫した形式で返すことが重要です。
成功レスポンスの設計
単一リソース取得(GET /api/v1/users/1)
{
"id": 1,
"name": "山田太郎",
"email": "taro@example.com",
"created_at": "2025-01-15T09:30:00Z",
"updated_at": "2025-06-20T14:00:00Z"
}
コレクション取得(GET /api/v1/users)
{
"data": [
{"id": 1, "name": "山田太郎", "email": "taro@example.com"},
{"id": 2, "name": "佐藤花子", "email": "hanako@example.com"}
],
"meta": {
"total": 50,
"page": 1,
"per_page": 20,
"total_pages": 3
}
}
リソース作成成功(POST /api/v1/users → 201 Created)
HTTP/1.1 201 Created
Location: /api/v1/users/3
{
"id": 3,
"name": "鈴木一郎",
"email": "ichiro@example.com",
"created_at": "2025-07-01T10:00:00Z"
}
エラーレスポンスの設計
エラーレスポンスは、クライアントが何が起きたかとどう対処すればいいかが分かるように設計します。
バリデーションエラー(422 Unprocessable Entity)
{
"error": {
"code": "VALIDATION_ERROR",
"message": "入力内容に問題があります",
"details": [
{
"field": "email",
"message": "メールアドレスの形式が正しくありません"
},
{
"field": "name",
"message": "名前は必須です"
}
]
}
}
認証エラー(401 Unauthorized)
{
"error": {
"code": "UNAUTHORIZED",
"message": "認証トークンが無効または期限切れです"
}
}
リソース未発見(404 Not Found)
{
"error": {
"code": "NOT_FOUND",
"message": "指定されたユーザーは存在しません"
}
}
エラーレスポンス設計のポイント
1. HTTPステータスコードを正しく使う:400番台はクライアントエラー、500番台はサーバーエラー2. エラーコードを一意にする:
VALIDATION_ERRORのような文字列コードで機械的に判別できるようにする3. 人間が読めるメッセージを含める:デバッグやユーザー表示に使える
4. フィールド単位のエラーを返す:フォームのどこが間違っているか特定できるようにする
よく使うHTTPステータスコード
| コード | 意味 | 使いどころ |
|---|---|---|
200 | OK | GET/PUT/PATCH成功 |
201 | Created | POST成功(リソース作成) |
204 | No Content | DELETE成功 |
400 | Bad Request | リクエスト形式が不正 |
401 | Unauthorized | 認証が必要 |
403 | Forbidden | 権限不足 |
404 | Not Found | リソースが存在しない |
422 | Unprocessable Entity | バリデーションエラー |
429 | Too Many Requests | レート制限超過 |
500 | Internal Server Error | サーバー内部エラー |
Step 5ページネーション・フィルタリング・ソート
大量のデータを返すAPIでは、ページネーション、フィルタリング、ソートの仕組みが不可欠です。これらはすべてクエリパラメータで制御します。
ページネーション
主に2つの方式があります。
| 方式 | パラメータ例 | 特徴 |
|---|---|---|
| オフセット方式 | ?page=2&per_page=20 | シンプル、ページジャンプ可能。大量データで遅くなる |
| カーソル方式 | ?cursor=abc123&limit=20 | 高パフォーマンス。ページジャンプ不可 |
オフセット方式のリクエストとレスポンス
# リクエスト
GET /api/v1/articles?page=2&per_page=10
# レスポンス
{
"data": [...],
"meta": {
"total": 95,
"page": 2,
"per_page": 10,
"total_pages": 10
},
"links": {
"first": "/api/v1/articles?page=1&per_page=10",
"prev": "/api/v1/articles?page=1&per_page=10",
"next": "/api/v1/articles?page=3&per_page=10",
"last": "/api/v1/articles?page=10&per_page=10"
}
}
カーソル方式のリクエストとレスポンス
# リクエスト(次ページ)
GET /api/v1/articles?cursor=eyJpZCI6MjB9&limit=10
# レスポンス
{
"data": [...],
"meta": {
"has_next": true,
"next_cursor": "eyJpZCI6MzB9"
}
}
どちらの方式を選ぶべきか
オフセット方式:管理画面、ブログ一覧など「ページ番号が必要」な場合カーソル方式:SNSのタイムライン、チャット履歴など「無限スクロール」の場合
データ量が数万件を超える場合はカーソル方式が推奨されます。
フィルタリング
フィルタリングの例
# カテゴリで絞り込み
GET /api/v1/articles?category=tech
# 複数条件で絞り込み
GET /api/v1/articles?category=tech&status=published
# 日付範囲で絞り込み
GET /api/v1/articles?created_after=2025-01-01&created_before=2025-12-31
# 検索
GET /api/v1/articles?search=REST+API
ソート
ソートの例
# 作成日の降順(新しい順)
GET /api/v1/articles?sort=-created_at
# タイトルの昇順
GET /api/v1/articles?sort=title
# 複合ソート(カテゴリ昇順 → 作成日降順)
GET /api/v1/articles?sort=category,-created_at
ソートの慣習
フィールド名の先頭に -(マイナス)を付けると降順を表すのが一般的な慣習です。Django REST Frameworkでもこの形式が使われています。
組み合わせ例
フィルタリング + ソート + ページネーション
# 公開済みの技術記事を新しい順に、1ページ10件で2ページ目を取得
GET /api/v1/articles?category=tech&status=published&sort=-created_at&page=2&per_page=10
Step 6バージョニングと認証
本番環境で長期運用するAPIでは、バージョン管理と認証の設計が不可欠です。
APIバージョニング
APIの仕様を変更する際、既存のクライアントを壊さないためにバージョンを管理します。
| 方式 | 例 | メリット | デメリット |
|---|---|---|---|
| URLパス方式 | /api/v1/users | 直感的で分かりやすい | URLが変わる |
| ヘッダー方式 | Accept: application/vnd.api+json;version=1 | URLがクリーン | 見えにくい |
| クエリパラメータ方式 | /api/users?version=1 | 実装が簡単 | キャッシュに不利 |
おすすめはURLパス方式
/api/v1/ の形式が最も広く使われています。GitHub、Stripe、Twitterなど主要なAPIサービスがこの方式を採用しています。シンプルで、ブラウザから直接テストしやすいのが利点です。
Django REST Frameworkでのバージョニング設定
# settings.py
REST_FRAMEWORK = {
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': ['v1', 'v2'],
}
# urls.py
urlpatterns = [
path('api/<version>/users/', UserListView.as_view()),
]
認証方式の比較
| 方式 | 仕組み | 適したケース |
|---|---|---|
| API Key | 固定のキーをヘッダーに付与 | サーバー間通信、シンプルなAPI |
| OAuth 2.0 | 認可サーバー経由でトークンを発行 | サードパーティ連携(Google, GitHub等) |
| JWT | 署名付きトークンで認証情報を伝達 | SPA、モバイルアプリ |
API Key 認証
API Keyをヘッダーで送信
curl -H "X-API-Key: your-api-key-here" https://api.example.com/v1/users
JWT(JSON Web Token)認証
JWTの認証フロー
# 1. ログインしてトークンを取得
POST /api/v1/auth/login
Content-Type: application/json
{
"email": "taro@example.com",
"password": "secure-password"
}
# レスポンス
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs...",
"expires_in": 3600
}
# 2. トークンを使ってAPIにアクセス
GET /api/v1/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
JWT運用時の注意点
1. アクセストークンの有効期限は短く(15分〜1時間)設定する2. リフレッシュトークンで新しいアクセストークンを再取得する仕組みを用意する
3. JWTにパスワードなどの機密情報を含めない(Base64でデコードすると中身が読める)
4. HTTPS必須 - トークンが平文で送られるため、HTTPでは盗聴される
OAuth 2.0 認証
OAuth 2.0 認可コードフロー
# 1. ユーザーを認可エンドポイントにリダイレクト
GET https://auth.example.com/authorize
?response_type=code
&client_id=your-client-id
&redirect_uri=https://your-app.com/callback
&scope=read+write
# 2. コールバックで認可コードを受け取る
GET https://your-app.com/callback?code=abc123
# 3. 認可コードをアクセストークンに交換
POST https://auth.example.com/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=abc123
&client_id=your-client-id
&client_secret=your-client-secret
&redirect_uri=https://your-app.com/callback
認証方式の選び方
内部APIや単純なサービス → API Keyユーザーログインが必要なSPA/モバイルアプリ → JWT
他サービスとの連携(GitHub, Google等) → OAuth 2.0
複数の認証方式を組み合わせることも一般的です。
まとめ
- RESTはHTTPの仕組みを活用したAPI設計のアーキテクチャスタイル
- URLはリソース(名詞・複数形)で表現し、操作はHTTPメソッドで区別する
- PUT(全体更新)とPATCH(部分更新)を正しく使い分ける
- 成功もエラーも一貫したJSON形式で返し、適切なステータスコードを使う
- ページネーション・フィルタリング・ソートはクエリパラメータで制御する
- バージョニングはURLパス方式が主流、認証はJWTまたはOAuth 2.0が一般的