JSON APIデザインパターン:より良いREST APIの構築
適切に設計されたAPIは使うのが楽しいものです。設計が不十分なものはバグ、フラストレーション、技術的負債を生みます。このガイドでは、一貫性があり、発見しやすく、保守しやすいJSON REST APIを設計するための実証済みパターンを紹介します。
リソース命名
リソースは名詞であり、動詞ではありません。コレクションには複数形名詞を、IDによる単一リソースには単数形を使用します:
GET /api/users # ユーザー一覧
POST /api/users # ユーザー作成
GET /api/users/123 # ユーザー123を取得
PUT /api/users/123 # ユーザー123を更新
DELETE /api/users/123 # ユーザー123を削除
命名規則:
- 小文字とハイフンを使用:
/api/blog-posts(blogPostsやblog_postsではなく) - 関連リソースをネスト:
/api/users/123/orders - ネストは2レベルまでに制限:
/api/users/123/orders/456(それ以上深くしない) - URLに動詞を避ける:CRUDにマッピングされないアクションの場合は
/api/users/123/activateは許容
レスポンスエンベロープ
一貫したエンベロープでレスポンスをラップします:
{
"data": {
"id": "123",
"type": "user",
"attributes": {
"name": "Alice",
"email": "alice@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
},
"meta": {
"requestId": "req_abc123"
}
}
コレクションの場合:
{
"data": [
{ "id": "123", "name": "Alice" },
{ "id": "456", "name": "Bob" }
],
"meta": {
"total": 142,
"page": 1,
"perPage": 20
}
}
JSONバリデーターでAPIレスポンスを検証し、スキーマに一致するか確認しましょう。
ページネーション
3つの一般的なアプローチ:
オフセットベース(最もシンプル)
GET /api/users?page=2&per_page=20
{
"data": [...],
"meta": {
"page": 2,
"perPage": 20,
"total": 142,
"totalPages": 8
}
}
利点: シンプル、任意のページにジャンプ可能。欠点: 同時挿入/削除で結果が不整合になる。
カーソルベース(最も信頼性が高い)
GET /api/users?cursor=eyJpZCI6MTIzfQ&limit=20
{
"data": [...],
"meta": {
"hasNext": true,
"nextCursor": "eyJpZCI6MTQzfQ"
}
}
利点: 同時変更でも一貫性あり、大規模データセットでパフォーマンスが良い。欠点: 任意のページにジャンプ不可。
キーセットベース(最もパフォーマンスが高い)
GET /api/users?after_id=123&limit=20
最後のアイテムのID(またはソートフィールド)を使用して次のページを取得します。カーソルベースに似ていますが、パラメーターが透過的です。
推奨: リアルタイムフィードや大規模データセットにはカーソルベース。ページジャンプが必要な管理ダッシュボードにはオフセットベース。
フィルタリングとソート
フィルタリング
GET /api/users?status=active&role=admin
GET /api/users?created_after=2024-01-01
GET /api/users?search=alice
複雑なフィルターには専用のクエリパラメーターを検討:
GET /api/users?filter[status]=active&filter[role]=admin
ソート
GET /api/users?sort=name # 昇順
GET /api/users?sort=-created_at # 降順(-をプレフィックス)
GET /api/users?sort=-created_at,name # 複数フィールド
エラーハンドリング
一貫したエラーレスポンスはAPIの使いやすさに不可欠です:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address",
"value": "not-an-email"
},
{
"field": "age",
"message": "Must be between 0 and 150",
"value": -5
}
]
},
"meta": {
"requestId": "req_abc123"
}
}
使用すべきHTTPステータスコード:
| コード | 意味 | 使用場面 |
|---|---|---|
| 200 | OK | 成功したGET、PUT |
| 201 | Created | 成功したPOST |
| 204 | No Content | 成功したDELETE |
| 400 | Bad Request | バリデーションエラー |
| 401 | Unauthorized | 認証なし/無効 |
| 403 | Forbidden | 認証は有効だが権限不足 |
| 404 | Not Found | リソースが存在しない |
| 409 | Conflict | リソースの重複、バージョン競合 |
| 422 | Unprocessable | 意味的に無効 |
| 429 | Too Many Requests | レート制限超過 |
| 500 | Internal Error | 予期しないサーバーエラー |
バージョニング
3つのアプローチ:
URLパス(推奨)
GET /api/v1/users
GET /api/v2/users
利点: 明示的、ルーティングが容易、テストが容易。
ヘッダーベース
GET /api/users
Accept: application/vnd.myapi.v2+json
利点: クリーンなURL。欠点: テストが困難、発見しにくい。
クエリパラメーター
GET /api/users?version=2
利点: テストが容易。欠点: オプションパラメーターのセマンティクス。
推奨: URLパスバージョニングが最も実用的です。明示的で、キャッシュ可能で、あらゆるHTTPツールで動作します。
日付と時間の処理
常にUTCのISO 8601を使用:
{
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T14:22:33Z",
"expiresAt": "2024-12-31T23:59:59Z"
}
APIレスポンスでUnixタイムスタンプを使用しないでください — 曖昧(秒かミリ秒か)で人間が読めません。タイムスタンプの処理について詳しくは、Unixタイムスタンプガイドをご覧ください。
レート制限
レスポンスヘッダーでレート制限を伝えます:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 67
X-RateLimit-Reset: 1705312800
Retry-After: 30
制限を超えた場合は429 Too Many Requestsを返し、明確なエラーメッセージとRetry-Afterヘッダーを含めます。
HATEOAS(オプションだが強力)
関連リソースとアクションへのリンクを含めます:
{
"data": {
"id": "123",
"name": "Alice",
"links": {
"self": "/api/users/123",
"orders": "/api/users/123/orders",
"avatar": "/api/users/123/avatar"
}
}
}
これによりAPIが自己文書化され、クライアント側でのURL構築が削減されます。
FAQ
JSON:API仕様を使うべきですか、それとも独自フォーマットを設計すべきですか?
JSON:API仕様(jsonapi.org)は包括的な標準を提供しますが、シンプルなAPIには冗長になる可能性があります。ほとんどのプロジェクトでは、このガイドのパターンに従ってシンプルなカスタムフォーマットを設計する方が実用的です。自動クライアントライブラリと厳密な標準が必要な場合はJSON:APIを使用してください。
部分更新はどう処理すべきですか(PATCH vs PUT)?
PUTは完全なリソース置換(クライアントがすべてのフィールドを送信)に使用します。PATCHは部分更新(クライアントが変更されたフィールドのみを送信)に使用します。JSON Merge Patch(RFC 7396)を使用したPATCHが最もシンプルなアプローチです:更新するフィールドのみを含むJSONオブジェクトを送信し、フィールドを削除するにはnullを指定します。
関連リソース
- JSONフォーマッター — 読みやすさのためにAPIレスポンスをフォーマット
- JSONスキーマバリデーションガイド — JSON SchemaでAPIペイロードを検証
- JWTトークン解説 — JSON Web TokenでAPIを保護