更新: 2026年03月 · 7 分で読める · 3,717 文字
REST API設計でバグを減らす5つのルール
REST APIの設計は、プロダクト品質とチーム開発効率を左右する重要な決定です。この記事では、実務で即座に導入できる5つのベストプラクティスを解説します。これらを実装することで、APIの保守性が向上し、クライアント側のバグも減らせます。
なぜREST API設計のベストプラクティスが必要か
一見シンプルに思えるREST APIでも、設計の甘さは後々大きな負債になります。以下のような問題が発生しやすいです:
- エンドポイント命名が統一されず、新しい開発者の学習コストが増加
- エラーレスポンスの形式がばらばらで、クライアント側のエラーハンドリングが複雑化
- バージョニング戦略がないため、既存クライアントを壊さずに仕様変更ができない
- レスポンスデータが肥大化し、ネットワーク性能に悪影響
これらの問題を予防するのがベストプラクティスの役割です。
1. URLの設計をリソース中心で統一する
良い設計と悪い設計の比較
REST APIの最大の特徴は、URLでリソースを表現することです。動詞ではなく名詞(リソース)を中心に設計してください。
| ❌ 避けるべき設計 | ✅ 推奨される設計 |
|---|---|
GET /getUsers |
GET /users |
POST /createUser |
POST /users |
GET /deleteUserById?id=123 |
DELETE /users/123 |
POST /user/123/updateProfile |
PATCH /users/123 |
実装例:Node.js + Express
推奨設計をExpress.jsで実装すると以下のようになります。
// ✅ RESTful な設計
app.get('/api/v1/users', (req, res) => {
// 全ユーザー取得
res.json({ users: [] });
});
app.get('/api/v1/users/:userId', (req, res) => {
// 特定ユーザー取得
const { userId } = req.params;
res.json({ user: { id: userId } });
});
app.post('/api/v1/users', (req, res) => {
// ユーザー作成(リクエストボディからデータを取得)
const newUser = req.body;
res.status(201).json({ user: newUser });
});
app.patch('/api/v1/users/:userId', (req, res) => {
// ユーザー情報の一部更新
const { userId } = req.params;
const updates = req.body;
res.json({ user: { id: userId, ...updates } });
});
app.delete('/api/v1/users/:userId', (req, res) => {
// ユーザー削除
const { userId } = req.params;
res.status(204).send();
});
2. HTTPステータスコードを正確に使い分ける
ステータスコードの使い分けが曖昧だと、クライアント側でのエラーハンドリングが複雑になります。よく使うコードの正しい使い方を紹介します。
| ステータスコード | 使用場面 |
|---|---|
200 OK |
GET, PUT, PATCH成功時。レスポンスボディあり |
201 Created |
POST成功時(新リソース作成)。Locationヘッダに新URLを含める |
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 |
サーバー側の予期しないエラー |
ステータスコード活用例
// ユーザー作成時のステータスコード使い分け
app.post('/api/v1/users', async (req, res) => {
try {
const { email, name } = req.body;
// バリデーション失敗(クライアント側の入力エラー)
if (!email || !name) {
return res.status(400).json({
error: 'Email and name are required'
});
}
// ビジネスロジックエラー(入力形式は正しいが処理できない)
const existingUser = await User.findByEmail(email);
if (existingUser) {
return res.status(422).json({
error: 'Email already exists'
});
}
// 正常作成
const newUser = await User.create({ email, name });
return res.status(201)
.header('Location', `/api/v1/users/${newUser.id}`)
.json({ user: newUser });
} catch (error) {
// 予期しないエラー
console.error(error);
return res.status(500).json({
error: 'Internal server error'
});
}
});
3. エラーレスポンスの形式を統一する
よくあるハマりポイント:エラー形式がばらばら
エラーレスポンスの形式が異なると、クライアント開発者が毎回コードを確認する羽目になります。APIサーバー全体で統一された形式を定義してください。
// ✅ 推奨:統一されたエラー形式
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Input validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
// ❌ 避けるべき:形式がばらばら
// ケース1
{ "message": "User not found" }
// ケース2
{ "error": "404 Not Found" }
// ケース3
{ "errors": ["Name is required", "Email is invalid"] }
エラーハンドリングミドルウェア実装
// グローバルエラーハンドリングミドルウェア
class ApiError extends Error {
constructor(code, message, statusCode = 400, details = []) {
super(message);
this.code = code;
this.statusCode = statusCode;
this.details = details;
}
}
app.use((err, req, res, next) => {
if (err instanceof ApiError) {
return res.status(err.statusCode).json({
error: {
code: err.code,
message: err.message,
details: err.details
}
});
}
// 予期しないエラー
console.error(err);
return res.status(500).json({
error: {
code: 'INTERNAL_SERVER_ERROR',
message: 'An unexpected error occurred'
}
});
});
// 使用例
app.post('/api/v1/users', (req, res, next) => {
try {
if (!req.body.email) {
throw new ApiError(
'VALIDATION_ERROR',
'Email is required',
400,
[{ field: 'email', message: 'This field is required' }]
);
}
} catch (error) {
next(error);
}
});
4. APIバージョニング戦略を実装する
バージョニングが必要な理由
APIは成長とともに仕様が変わります。既存クライアントを壊さずに新機能を追加するには、バージョニングが必須です。
| バージョニング方式 | メリット | デメリット |
|---|---|---|
URL パス(推奨)/api/v1/users |
最もシンプル。バージョンが目に見える | 複数バージョンの並行保守が必要 |
URLクエリ/api/users?version=1 |
URLはシンプル。デフォルト版の指定が可能 | バージョン指定忘れのリスク |
HeaderベースAccept: application/vnd.myapi.v1+json |
𝕏 ポスト
Facebook
LINE
|