Node.js レッスン4
REST API設計
CRUD操作とHTTPメソッドでAPIを設計しよう
RESTとは?
REST(Representational State Transfer)は、 Web APIを設計するためのアーキテクチャスタイルです。 HTTPメソッドとURLの組み合わせでリソース(データ)を操作します。
RESTの原則
- • URLはリソース(名詞)を表す
- • HTTPメソッドで操作(動詞)を表す
- • ステートレス(状態を持たない)
- • 統一されたインターフェース
URL設計の例
GET /api/books— 一覧取得GET /api/books/1— 詳細取得POST /api/books— 新規作成PUT /api/books/1— 更新DELETE /api/books/1— 削除
HTTPメソッドとステータスコード
REST APIでは、HTTPメソッドで操作の種類を、ステータスコードで結果を表現します。
// === HTTPメソッド ===
// GET - リソースの取得(べき等・安全)
// POST - リソースの作成
// PUT - リソースの全体更新(べき等)
// PATCH - リソースの部分更新
// DELETE - リソースの削除(べき等)
// === よく使うステータスコード ===
// 200 OK - リクエスト成功
// 201 Created - リソース作成成功
// 204 No Content - 成功(レスポンスボディなし)
// 400 Bad Request - クライアントのリクエストが不正
// 401 Unauthorized - 認証が必要
// 403 Forbidden - アクセス権限がない
// 404 Not Found - リソースが見つからない
// 409 Conflict - リソースの競合
// 422 Unprocessable - バリデーションエラー
// 500 Internal Error - サーバー内部エラーCRUD操作の実装
書籍管理APIを例に、CRUD(Create, Read, Update, Delete)操作を実装してみましょう。
const express = require('express');
const app = express();
app.use(express.json());
// 仮のデータベース(メモリ上)
let books = [
{ id: 1, title: 'JavaScript入門', author: '山田太郎', price: 2800 },
{ id: 2, title: 'Node.js実践ガイド', author: '鈴木花子', price: 3200 },
{ id: 3, title: 'Web開発の教科書', author: '田中一郎', price: 2500 },
];
let nextId = 4;
// CREATE: 書籍を新規作成
app.post('/api/books', (req, res) => {
const { title, author, price } = req.body;
// バリデーション
if (!title || !author) {
return res.status(400).json({
error: 'title と author は必須です',
});
}
const newBook = {
id: nextId++,
title,
author,
price: price || 0,
};
books.push(newBook);
res.status(201).json(newBook);
});
// READ: 書籍一覧を取得
app.get('/api/books', (req, res) => {
// クエリパラメータでフィルタリング
const { author, minPrice } = req.query;
let result = books;
if (author) {
result = result.filter(b => b.author.includes(author));
}
if (minPrice) {
result = result.filter(b => b.price >= Number(minPrice));
}
res.json(result);
});
// READ: 書籍を1件取得
app.get('/api/books/:id', (req, res) => {
const book = books.find(b => b.id === Number(req.params.id));
if (!book) {
return res.status(404).json({ error: '書籍が見つかりません' });
}
res.json(book);
});
// UPDATE: 書籍を更新
app.put('/api/books/:id', (req, res) => {
const index = books.findIndex(b => b.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: '書籍が見つかりません' });
}
const { title, author, price } = req.body;
books[index] = {
...books[index],
title: title ?? books[index].title,
author: author ?? books[index].author,
price: price ?? books[index].price,
};
res.json(books[index]);
});
// DELETE: 書籍を削除
app.delete('/api/books/:id', (req, res) => {
const index = books.findIndex(b => b.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: '書籍が見つかりません' });
}
books.splice(index, 1);
res.status(204).send();
});
app.listen(3000);リクエストボディのパースとバリデーション
POSTやPUTリクエストではクライアントからデータを受け取ります。 不正なデータからアプリを守るためにバリデーションが重要です。
// express.json() でJSONボディをパース
app.use(express.json());
// URLエンコードされたデータもパース(フォーム送信等)
app.use(express.urlencoded({ extended: true }));
// 手動バリデーションの例
function validateBook(data) {
const errors = [];
if (!data.title || typeof data.title !== 'string') {
errors.push('title は文字列で必須です');
}
if (!data.author || typeof data.author !== 'string') {
errors.push('author は文字列で必須です');
}
if (data.price !== undefined && typeof data.price !== 'number') {
errors.push('price は数値で指定してください');
}
if (data.price !== undefined && data.price < 0) {
errors.push('price は0以上で指定してください');
}
return errors;
}
app.post('/api/books', (req, res) => {
const errors = validateBook(req.body);
if (errors.length > 0) {
return res.status(422).json({ errors });
}
// バリデーション通過 → 作成処理
const newBook = {
id: nextId++,
title: req.body.title.trim(),
author: req.body.author.trim(),
price: req.body.price || 0,
};
books.push(newBook);
res.status(201).json(newBook);
});API設計のベストプラクティス
使いやすく、保守しやすいAPIを作るためのポイントを押さえましょう。
// 1. URLは名詞・複数形を使う
// Good: /api/users, /api/books
// Bad: /api/getUsers, /api/book
// 2. ネストでリレーションを表現
// GET /api/users/1/posts ← ユーザー1の投稿一覧
// GET /api/posts/5/comments ← 投稿5のコメント一覧
// 3. ページネーション
app.get('/api/books', (req, res) => {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const start = (page - 1) * limit;
const paginatedBooks = books.slice(start, start + limit);
res.json({
data: paginatedBooks,
pagination: {
page,
limit,
total: books.length,
totalPages: Math.ceil(books.length / limit),
},
});
});
// 4. 統一されたエラーレスポンス
// {
// "error": {
// "code": "NOT_FOUND",
// "message": "書籍が見つかりません"
// }
// }
// 5. APIバージョニング
// /api/v1/users
// /api/v2/usersまとめ
- RESTはHTTPメソッド(GET/POST/PUT/DELETE)でリソースを操作するアーキテクチャ
- 適切なステータスコード(200, 201, 404, 500等)で結果を伝える
- CRUD操作はWebアプリの基本パターン
- バリデーションで不正なデータからアプリを守る
- URL設計はリソース名(名詞・複数形)を使い、ページネーションやバージョニングを考慮する