API設計
適切なAPIタイプの選び方
| タイプ | 最適な用途 | 避けるべきケース |
|---|
| REST | 公開API、CRUD、シンプルなクライアント、IoT | 複雑なネストデータ、頻繁なスキーマ変更 |
| GraphQL | モバイルアプリ、複雑なネストデータ、複数クライアント | シンプルなCRUD、経験の浅い小規模チーム |
| tRPC | TypeScriptモノレポ、内部のフルスタックTS API | TypeScript以外のクライアント、公開API |
| gRPC | 高パフォーマンスなマイクロサービス間通信、ストリーミング | ブラウザクライアント、単純なユースケース |
REST API設計
URLの規則
GET /users ← 一覧
GET /users/{id} ← 単一
POST /users ← 作成
PUT /users/{id} ← 置き換え
PATCH /users/{id} ← 部分更新
DELETE /users/{id} ← 削除
# ネストされたリソース
GET /users/{id}/orders
POST /users/{id}/orders
# アクション(REST動詞で不足する場合)
POST /orders/{id}/cancel
POST /users/{id}/activate
HTTPステータスコード
| コード | 使用タイミング |
|---|
| 200 | GET、PUT、PATCHの成功時 |
| 201 | POSTによる作成成功時 |
| 204 | DELETE成功時(ボディなし) |
| 400 | バリデーション失敗、不正なリクエスト |
| 401 | 認証情報の欠如・不正 |
| 403 | 認証済みだが権限なし |
| 404 | リソースが見つからない |
| 409 | 競合(重複、バージョン不一致) |
| 422 | 処理不能なエンティティ(意味的エラー) |
| 429 | レート制限超過 |
| 500 | 予期しないサーバーエラー |
レスポンスエンベロープ
{
"data": [...],
"meta": { "total": 100, "page": 1, "perPage": 20 }
}
{ "data": { "id": "1", "name": "James" } }
{
"error": {
"code": "VALIDATION_ERROR",
"message": "バリデーションに失敗しました",
"details": [{ "field": "email", "issue": "不正な形式です" }]
}
}
API設計原則
バージョニング戦略
# URLバージョニング(最も視認性が高く、ルーティングしやすい)
/v1/users
/v2/users
# ヘッダーバージョニング(クリーンなURL)
Accept: application/vnd.api+json;version=2
既存のクライアントを決して壊さないでください。非推奨にしてから削除します。
ページネーション
# オフセット方式(シンプル、小規模データセットに適する)
GET /posts?page=2&perPage=20
# カーソル方式(大規模データセットで高速、無限スクロールに使用)
GET /posts?cursor=eyJpZCI6MTAwfQ&limit=20
フィルタリングと並び替え
GET /orders?status=pending&userId=123
GET /products?sort=-price,name # - 接頭辞 = 降順
GET /products?fields=id,name,price # スパースフィールドセット
べき等性
# 非べき等操作にはべき等キーを含める
POST /payments
Idempotency-Key: unique-client-generated-uuid
GraphQL設計
スキーマ設計規則
- DB構造ではなくビジネスドメインを記述する
- リストにはコネクションパターンを使用する(ページネーション対応)
- ミューテーションは変更されたオブジェクトを返す
- 有限な値セットには列挙型を使用する
- すべての型とフィールドに説明を追加する
type Query {
user(id: ID!): User
users(filter: UserFilter, pagination: PaginationInput): UserConnection!
}
type Mutation {
createUser(input: CreateUserInput!): CreateUserPayload!
updateUser(id: ID!, input: UpdateUserInput!): UpdateUserPayload!
}
type UserConnection {
edges: [UserEdge!]!
pageInfo: PageInfo!
totalCount: Int!
}
APIセキュリティチェックリスト