Fastify ベストプラクティス #5: Swaggerドキュメントの生成
サーバーサイドAPIを開発する際、正確かつリアルタイムで理解しやすいAPIドキュメントを維持することは、チームの協力効率と外部開発者の体験を向上させる鍵となります。手作業でのドキュメント作成は時間がかかるだけでなく、コードとドキュメントの不整合が非常に起こりやすいです。幸いなことに、Fastifyのエコシステムには@fastify/swaggerと@fastify/swagger-uiという2つの強力なツールがあり、これらを使うことで私たちのコード(特にZodスキーマ)を直接、プロフェッショナルでインタラクティブなSwaggerドキュメントに変換し、「コード即ドキュメント」を実現できます。
まず、関連する依存関係をインストールする必要があります。
npm install @fastify/swagger @fastify/swagger-ui
Swaggerプラグインの設定
次に、Fastifyインスタンスにこれら2つのプラグインを登録します。独立したプラグインファイル(例: plugins/swagger.ts)で設定を行うのが良いプラクティスです。
ここでの核心は、@fastify/swaggerが、私たちが前回の記事で強く推奨したZodスキーマを理解できるようにすることです。
import swagger from '@fastify/swagger';
import swaggerUi from '@fastify/swagger-ui';
import fastify, { FastifyInstance } from 'fastify';
import { jsonSchemaTransform } from 'fastify-type-provider-zod';
export const setupSwagger = async (server: FastifyInstance) => {
// swaggerコアプラグインを登録
await server.register(swagger, {
// 重要:swaggerがZodスキーマをサポートするようにする
transform: jsonSchemaTransform,
openapi: {
openapi: '3.0.0', // OpenAPIバージョン
info: {
// ドキュメントの基本情報
title: 'Fastify ベストプラクティスプロジェクト API',
description: 'これはサンプルプロジェクトのAPIドキュメントです',
version: '0.1.0',
},
servers: [
// APIサーバーのリスト
{
url: `http://127.0.0.1:8080`,
description: '開発サーバー',
},
],
tags: [
// API分類タグ
{ name: 'user', description: 'ユーザー関連API' },
{ name: 'health', description: 'ヘルスチェックAPI' },
],
// オプション:グローバルなセキュリティ認証設定を追加
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
},
},
security: [
{
bearerAuth: [],
},
],
},
});
// swagger UIプラグインを登録し、可視化されたドキュメント画面を提供
await server.register(swaggerUi, {
routePrefix: '/doc', // swaggerドキュメントのアクセスパス
uiConfig: {
docExpansion: 'list', // 'full', 'list', 'none'
deepLinking: true,
},
});
};
jsonSchemaTransformの役割
お気づきかもしれませんが、ここでの鍵はtransform: jsonSchemaTransformという設定です。fastify-type-provider-zodからインポートされるこの関数は、いわば橋渡しのような役割を果たします。ルートで定義したZodスキーマを、Swagger(OpenAPI仕様)が要求するJSON Schema形式に自動的に変換してくれるのです。これがなければ、Swaggerは私たちのZod型定義を理解できません。
ルートにドキュメント情報を追加する
プラグインの設定が完了したら、次は各APIルートのschemaオプションにメタデータを追加する必要があります。Swaggerはこれらの情報を読み取ってドキュメントを生成します。
具体的な例を見てみましょう。
import { FastifyInstance } from 'fastify';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod';
const routes = async (fastify: FastifyInstance) => {
fastify.withTypeProvider<ZodTypeProvider>().get(
'/user/:id', // APIのURL
{
schema: {
summary: '単一ユーザー情報の取得',
description: 'ユーザーIDに基づいて詳細情報を照会します。',
tags: ['user'], // APIの分類
params: z.object({
id: z.string().uuid().describe('ユーザーの一意ID (UUID形式)'),
}),
response: {
200: z
.object({
id: z.string().uuid(),
name: z.string(),
email: z.string().email(),
})
.describe('成功レスポンス'),
404: z
.object({
message: z.string(),
})
.describe('ユーザーが見つからない場合のレスポンス'),
},
// このAPIがbearerAuth認証を必要とすることを示す
security: [{ bearerAuth: [] }],
},
},
async (req, reply) => {
// ... ビジネスロジック ...
const user = { id: req.params.id, name: 'きかちゃん', email: 'kika-chan@example.com' };
return reply.status(200).send(user);
},
);
};
export default routes;
ヒント:Zodの.describe()メソッドは非常に便利なテクニックです。jsonSchemaTransformは賢くその内容を読み取り、Swagger UIのフィールド説明として表示してくれるため、APIドキュメントがより明確になります。
まとめ
上記の設定を通じて、私たちはプロジェクトのためにプロフェッショナルでインタラクティブなAPIドキュメントを生成しただけではありません。最も重要なのは、このドキュメントが私たちのコードと完全に同期した「生きたドキュメント」であるということです。あなたのZodスキーマが変更されるたびに、ドキュメントは自動的に更新されます。これにより、手作業でのドキュメント管理に起因するコードとドキュメントの不整合という悩みから完全に解放されます。これは開発効率とAPIの保守性を大幅に向上させ、現代のサーバーサイド開発において不可欠な要素です。
私のオープンソースプロジェクトでは、Swaggerに関する設定を独立したプラグイン(04.swagger.plugin.ts)にカプセル化し、fastify-autoloadを通じて自動的に読み込んでいます。よろしければ参考にしてください。