第9章
FastifyでCRUD APIを作る — Todo APIを組み立てる
約3分
ここまで学んだルーティング、request/reply、schema、エラーハンドリングを使って、Todo APIを組み立てます。この章ではDBには接続せず、インメモリ配列で進めます。DB接続は後から差し替えられるよう、まずAPIの形を固めます。

アプリの入口
src/app.js でFastifyインスタンスを作ります。
import Fastify from 'fastify';
import { todoRoutes } from './routes/todos.js';
export function buildApp() {
const fastify = Fastify({ logger: true });
fastify.register(todoRoutes, { prefix: '/todos' });
fastify.setNotFoundHandler((request, reply) => {
return reply.code(404).send({
error: { code: 'ROUTE_NOT_FOUND', message: '存在しないURLです' },
});
});
return fastify;
}js
src/server.js はlistenだけを担当します。
import { buildApp } from './app.js';
const app = buildApp();
try {
await app.listen({ port: 3000 });
} catch (err) {
app.log.error(err);
process.exit(1);
}js
この分割にしておくと、テストの章で buildApp() を直接呼べます。
Todoルート
src/routes/todos.js を作ります。
const todos = [
{ id: 1, title: 'Fastifyを学ぶ', completed: false },
];
const todoSchema = {
type: 'object',
properties: {
id: { type: 'integer' },
title: { type: 'string' },
completed: { type: 'boolean' },
},
};
const idParamsSchema = {
type: 'object',
required: ['id'],
properties: {
id: { type: 'integer', minimum: 1 },
},
};
const createTodoBodySchema = {
type: 'object',
required: ['title'],
additionalProperties: false,
properties: {
title: { type: 'string', minLength: 1, maxLength: 100 },
},
};js
schemaを変数に分けると、ルート定義が読みやすくなります。
一覧取得と作成
export async function todoRoutes(fastify) {
fastify.get('/', {
schema: {
response: {
200: {
type: 'array',
items: todoSchema,
},
},
},
}, async () => {
return todos;
});
fastify.post('/', {
schema: {
body: createTodoBodySchema,
response: {
201: todoSchema,
},
},
}, async (request, reply) => {
const todo = {
id: todos.length + 1,
title: request.body.title,
completed: false,
};
todos.push(todo);
return reply.code(201).send(todo);
});
}js
1件取得、更新、削除
同じ todoRoutes の中に追加します。
fastify.get('/:id', {
schema: {
params: idParamsSchema,
response: {
200: todoSchema,
},
},
}, async (request, reply) => {
const todo = todos.find((item) => item.id === request.params.id);
if (!todo) {
return reply.code(404).send({ error: 'TODO_NOT_FOUND' });
}
return todo;
});
fastify.patch('/:id', {
schema: {
params: idParamsSchema,
body: {
type: 'object',
additionalProperties: false,
properties: {
title: { type: 'string', minLength: 1, maxLength: 100 },
completed: { type: 'boolean' },
},
},
response: {
200: todoSchema,
},
},
}, async (request, reply) => {
const todo = todos.find((item) => item.id === request.params.id);
if (!todo) {
return reply.code(404).send({ error: 'TODO_NOT_FOUND' });
}
Object.assign(todo, request.body);
return todo;
});
fastify.delete('/:id', {
schema: {
params: idParamsSchema,
},
}, async (request, reply) => {
const index = todos.findIndex((item) => item.id === request.params.id);
if (index === -1) {
return reply.code(404).send({ error: 'TODO_NOT_FOUND' });
}
todos.splice(index, 1);
return reply.code(204).send();
});js
DBに差し替えるときの考え方
今は配列を直接操作していますが、実務ではDBやRepository層に置き換えます。重要なのは、HTTPの入口とデータ保存の詳細を混ぜすぎないことです。
route handler
↓
todo service
↓
database参考リンク
次章では、このTodo APIを fastify.inject() でテストします。
Node.jsクイズに挑戦するAPI、JSON、非同期処理の基礎をクイズで確認しよう
