こんにちは、かつコーチです。
ルーティングやミドルウェアを学んだら、次はいよいよ実践です。
この記事では、データベースを使わずに、配列(インメモリデータ)を使ったシンプルなREST APIを作りながら、CRUDの基本を身につけます。
CRUDとは、Create(作成)、Read(取得)、Update(更新)、Delete(削除)の頭文字を取った言葉で、Webアプリの基本操作を表します。
DB接続は登場しないので、Node.jsとExpressの基礎だけで手を動かして理解できる内容になっています。
基本の書き方 / 実装手順
準備:プロジェクトのセットアップ
まずはプロジェクトを作成し、Expressをインストールします。
mkdir todo-api
cd todo-api
npm init -y
npm install express
今回は、シンプルな「ToDoリストAPI」を題材にします。
手順1:インメモリデータを用意する
データベースの代わりに、配列でタスクのデータを保持します。
// server.js
const express = require('express');
const app = express();
app.use(express.json());
// インメモリのデータストア(サーバーを再起動すると消える)
let todos = [
{ id: 1, title: '牛乳を買う', done: false },
{ id: 2, title: 'レポートを提出する', done: false },
];
let nextId = 3; // 次に採番するID
手順2:GET(一覧取得・詳細取得)
まずは、Read(取得)のエンドポイントから作ります。
// 一覧取得:GET /todos
app.get('/todos', (req, res) => {
res.json(todos);
});
// 詳細取得:GET /todos/:id
app.get('/todos/:id', (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'タスクが見つかりません' });
}
res.json(todo);
});
req.params.idはURLのパスパラメータのため、常に文字列型で渡されます。
配列内のデータは数値型のIDなので、Number()で変換してから比較する必要がある点に注意してください。
手順3:POST(新規作成)
次に、Create(作成)のエンドポイントを作ります。
// 新規作成:POST /todos
app.post('/todos', (req, res) => {
const { title } = req.body;
if (!title) {
return res.status(400).json({ error: 'titleは必須です' });
}
const newTodo = { id: nextId++, title, done: false };
todos.push(newTodo);
// 作成成功時は201 Createdを返すのが慣例
res.status(201).json(newTodo);
});
作成が成功したときのステータスコードは、200ではなく201(Created)を返すのがREST APIの慣例です。
手順4:PUT(更新)
続いて、Update(更新)のエンドポイントです。
// 更新:PUT /todos/:id
app.put('/todos/:id', (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
if (!todo) {
return res.status(404).json({ error: 'タスクが見つかりません' });
}
const { title, done } = req.body;
if (title !== undefined) todo.title = title;
if (done !== undefined) todo.done = done;
res.json(todo);
});
done !== undefinedのようにチェックしているのは、req.bodyに含まれていない項目まで上書きしないようにするためです。
手順5:DELETE(削除)
最後に、Delete(削除)のエンドポイントです。
// 削除:DELETE /todos/:id
app.delete('/todos/:id', (req, res) => {
const index = todos.findIndex((t) => t.id === Number(req.params.id));
if (index === -1) {
return res.status(404).json({ error: 'タスクが見つかりません' });
}
todos.splice(index, 1);
// 削除成功時はボディなしの204 No Contentを返すのが一般的
res.status(204).send();
});
app.listen(3000, () => {
console.log('ToDo APIを起動しました: http://localhost:3000');
});
削除に成功したときは、返すデータがないため204 No Contentを返すのが一般的です。
これで、GET・POST・PUT・DELETEがそろった、最小限のCRUD APIが完成しました。
よくあるつまずきポイント・エラー対処
IDの型がずれて検索がヒットしない
CRUD APIを初めて作ったとき、一番ハマりやすいのがID比較の型不一致です。
// ❌ Before:型変換せずに厳密等価で比較する
app.get('/todos/:id', (req, res) => {
const todo = todos.find((t) => t.id === req.params.id); // 常にfalseになる
// ...
});
req.params.idは文字列("1")ですが、配列内のidは数値(1)です。
===は型まで含めて比較するため、1 === "1"はfalseとなり、データが存在するのに404が返り続けてしまいます。
私も実際にこのバグに遭遇し、「配列には確かにデータがあるのに、なぜ見つからないんだ」と原因を探すのに時間を使いました。
// ✅ After:Number()で型を揃えてから比較する
app.get('/todos/:id', (req, res) => {
const todo = todos.find((t) => t.id === Number(req.params.id));
// ...
});
パスパラメータを使って配列やDBのIDと照合するときは、必ず型変換を意識してください。
POSTしたのにtitleがundefinedになる
もう1つのよくある失敗が、express.json()の登録忘れです。
// ❌ Before:express.json()を登録していない
const express = require('express');
const app = express();
app.post('/todos', (req, res) => {
console.log(req.body.title); // undefined
// ...
});
このコードでPOSTリクエストを送ると、req.body自体がundefinedになり、req.body.titleを読み取ろうとした瞬間に以下のエラーが発生します。
TypeError: Cannot read properties of undefined (reading 'title')
// ✅ After:app.use(express.json())を先に登録する
const express = require('express');
const app = express();
app.use(express.json());
app.post('/todos', (req, res) => {
console.log(req.body.title); // 正しく取得できる
// ...
});
POST・PUTのエンドポイントを作る前には、必ずapp.use(express.json())を書いたか確認する習慣をつけましょう。
まとめ
この記事のポイント
- CRUDは、Create・Read・Update・Deleteの4つの基本操作
- GET(一覧・詳細)、POST(作成)、PUT(更新)、DELETE(削除)でエンドポイントを構成する
- パスパラメータの
req.params.idは文字列なので、比較前にNumber()で変換する - 作成成功は
201、削除成功は204など、ステータスコードの慣例を意識する express.json()の登録忘れは、req.bodyがundefinedになる典型的な原因
次に読むべき記事
- ルーターを分割して整理する(express.Router()の使い方)
- エラーハンドリングミドルウェアで一元的にエラーを処理する
- ExpressアプリをRenderにデプロイする方法
タグ: Express, 中級者向け, 実践Tips