API設計
REST APIは、フロントエンドとバックエンドをつなぐ重要なインターフェース。一貫性のある設計がアプリの品質を左右します。
RESTful API設計の基本
HTTPメソッドとCRUD
| メソッド | 操作 | 例 | 説明 |
|---|---|---|---|
| GET | Read(取得) | GET /posts | 記事一覧を取得 |
| POST | Create(作成) | POST /posts | 新規記事を作成 |
| PUT | Update(更新) | PUT /posts/1 | 記事を全体更新 |
| PATCH | Update(部分更新) | PATCH /posts/1 | 記事を部分更新 |
| DELETE | Delete(削除) | DELETE /posts/1 | 記事を削除 |
URL設計のルール
○
/posts
複数形の名詞を使う
✗
/getPost, /createPost
動詞は使わない
○
/posts/1/comments
関連リソースは階層で表現
○
/posts?page=1&limit=10
フィルタ・ページングはクエリパラメータ
ブログアプリのAPI設計
認証API
| エンドポイント | 説明 | 認証 |
|---|---|---|
| POST /auth/register | ユーザー新規登録 | 不要 |
| POST /auth/login | ログイン(トークン発行) | 不要 |
| GET /auth/me | 現在のユーザー情報 | 必要 |
記事API
| エンドポイント | 説明 | 認証 |
|---|---|---|
| GET /posts | 記事一覧取得 | 不要 |
| GET /posts/:id | 記事詳細取得 | 不要 |
| POST /posts | 記事作成 | 必要 |
| PUT /posts/:id | 記事更新 | 必要(本人のみ) |
| DELETE /posts/:id | 記事削除 | 必要(本人のみ) |
リクエストとレスポンスの例
POST /auth/register - ユーザー登録
リクエスト
{
"email": "user@example.com",
"password": "securepassword",
"name": "山田太郎"
}
レスポンス(201 Created)
{
"id": 1,
"email": "user@example.com",
"name": "山田太郎",
"createdAt": "2024-01-15T10:30:00Z"
}
POST /auth/login - ログイン
リクエスト
{
"email": "user@example.com",
"password": "securepassword"
}
レスポンス(200 OK)
{
"access_token": "eyJhbGciOiJ...",
"token_type": "bearer"
}
GET /posts - 記事一覧
レスポンス(200 OK)
{
"posts": [
{
"id": 1,
"title": "はじめての投稿",
"content": "これは最初の記事です...",
"author": {
"id": 1,
"name": "山田太郎"
},
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
],
"total": 1,
"page": 1,
"limit": 10
}
HTTPステータスコード
成功(2xx)
- 200 OK - 成功
- 201 Created - 作成成功
- 204 No Content - 削除成功
エラー(4xx, 5xx)
- 400 Bad Request - リクエスト不正
- 401 Unauthorized - 認証必要
- 403 Forbidden - 権限なし
- 404 Not Found - 見つからない
- 500 Internal Error - サーバーエラー
FastAPIでの実装例
from fastapi import FastAPI, HTTPException, Depends
from pydantic import BaseModel
app = FastAPI()
# リクエスト/レスポンスの型定義
class PostCreate(BaseModel):
title: str
content: str
class PostResponse(BaseModel):
id: int
title: str
content: str
author_id: int
created_at: datetime
# 記事一覧
@app.get("/posts", response_model=list[PostResponse])
def get_posts(page: int = 1, limit: int = 10):
# データベースから記事を取得
return posts
# 記事詳細
@app.get("/posts/{post_id}", response_model=PostResponse)
def get_post(post_id: int):
post = get_post_by_id(post_id)
if not post:
raise HTTPException(status_code=404, detail="Post not found")
return post
# 記事作成(認証必要)
@app.post("/posts", response_model=PostResponse, status_code=201)
def create_post(post: PostCreate, current_user = Depends(get_current_user)):
new_post = create_post_in_db(post, current_user.id)
return new_post
# 記事削除(本人のみ)
@app.delete("/posts/{post_id}", status_code=204)
def delete_post(post_id: int, current_user = Depends(get_current_user)):
post = get_post_by_id(post_id)
if not post:
raise HTTPException(status_code=404, detail="Post not found")
if post.author_id != current_user.id:
raise HTTPException(status_code=403, detail="Not authorized")
delete_post_from_db(post_id)
return None
まとめ
- ✓ HTTPメソッドを適切に使い分ける(GET, POST, PUT, DELETE)
- ✓ URLは複数形の名詞を使う(/posts, /users)
- ✓ 適切なステータスコードを返す
- ✓ 認証が必要なエンドポイントを明確にする