Closed7
Next.js + OpenAPI3-TS + Zodで、APIドキュメント(swagger)をメンテナンスフリーにする
自分の基礎能力を向上させるのが目的のため、ChatGPTやCursorは極力使用禁止とする。
スクラップの目的
現状
- Next.jsでAPIを開発している
- 開発速度とトレードオフに、ドキュメントがメンテナンスされていない問題がある。
やりたいこと
- OpenAPI3-TSを使用することで、APIのドキュメントが自動生成出来るか試す。
- Zod to OpenAPIを使用することで、APIのinputを制御出来るか試す。
各種ドキュメント
OpenAPI3-TS
Zod to Open API
環境
npx create-next-app@latest --typescriptでNext.jsのプロジェクトを作成。
latestはv16.0.0でした。
- Next.js:v16.0.0
- Node.js:v24.x
OpenAPI3-TSとZod to Open APIとZodをインストール
npm i openapi3-ts @asteasolutions/zod-to-openapi zodを実行

ZodにOpenAPIを拡張
- Zod to Open APIの公式ドキュメントになるべく従いながら作成していく。
-
extendZodWithOpenApiを使用してZodにopenapiを付けれるようにする。 - この
.openapiがschemaのExampleコードになるみたい。
src/schemas/user-schema.ts
import {extendZodWithOpenApi} from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';
extendZodWithOpenApi(z);
const TaskSchema = z
.object({
id: z.string().openapi({ example: '1212121' }),
name: z.string().openapi({ example: 'Example Task' }),
completed: z.boolean().openapi({ example: false }),
})
.openapi('Task');
Example Codeを見ながら進める
OpenAPI用のドキュメントを書く
-
new OpenAPIRegistry();を使用して、OpenAPIの生成に必要な各種情報を登録する。 - GETとPOSTを用意した。
src/schemas/todo-schema.ts
import {extendZodWithOpenApi, OpenAPIRegistry} from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';
extendZodWithOpenApi(z);
export const registry = new OpenAPIRegistry();
/*
- GET /api/tasks - タスク取得
*/
const TaskResponseSchema = z
.object({
id: z.string().openapi({ example: '1212121' }),
name: z.string().openapi({ example: 'Example Task' }),
completed: z.boolean().openapi({ example: false }),
})
.openapi('Task');
registry.registerPath({
method: "get",
path: "/api/tasks",
tags: ["task"],
summary: "タスク取得",
description: "登録されているタスクを返却します",
request: {},
responses: {
200: {
description: "タスクの取得成功",
content: {
"application/json": {
schema: TaskResponseSchema,
},
},
},
},
});
/*
- POST /api/tasks - タスク登録
*/
const CreateTaskSchema = z
.object({
name: z.string().openapi({ example: 'Example Task' }),
}).openapi('Task');
registry.registerPath({
method: "post",
path: "/api/tasks",
tags: ["task"],
summary: "タスク登録",
description: "タスクを登録します",
request: {
body: {
content: {
"application/json": {
schema: CreateTaskSchema,
},
},
},
},
responses: {
200: {
description: "タスクの取得成功",
content: {
"application/json": {
schema: TaskResponseSchema,
},
},
},
},
});
OpenAPIのYamlが出力されるようにする
getOpenApiDocumentationとwriteDocumentationを設定する
ExampleCodeは__dirnameを使用しているが、ESMでは使えないためprocess.cwd()を使用する。
import {extendZodWithOpenApi, OpenApiGeneratorV3, OpenAPIRegistry} from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';
import * as yaml from 'yaml';
import * as fs from 'fs';
import path from "node:path";
extendZodWithOpenApi(z);
export const registry = new OpenAPIRegistry();
...
function getOpenApiDocumentation() {
const generator = new OpenApiGeneratorV3(registry.definitions);
return generator.generateDocument({
openapi: '3.0.0',
info: {
version: '1.0.0',
title: 'My API',
description: 'This is the API',
},
servers: [{ url: 'v1' }],
});
}
function writeDocumentation() {
// OpenAPI JSON
const docs = getOpenApiDocumentation();
// YAML equivalent
const fileContent = yaml.stringify(docs);
const outPath = path.resolve(process.cwd(), "public/openapi-docs.yml");
fs.writeFileSync(outPath, fileContent, {
encoding: 'utf-8',
});
}
writeDocumentation();
package.jsonにスクリプトを登録
-
package.jsonにgenerate-docsのスクリプトを登録
package.json
{
"name": "example-auto-generate-openapi3-docs",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"generate-docs": "node src/schemas/task-schema.ts",
"lint": "eslint"
},
...
}
npm run generate-docsを実行
- 出力の確認
出力
openapi: 3.0.0
info:
version: 1.0.0
title: My API
description: This is the API
servers:
- url: v1
components:
schemas:
Task:
type: object
properties:
id:
type: string
example: "1212121"
name:
type: string
example: Example Task
completed:
type: boolean
example: false
required:
- id
- name
- completed
parameters: {}
paths:
/api/tasks:
get:
tags:
- task
summary: タスク取得
description: 登録されているタスクを返却します
responses:
"200":
description: タスクの取得成功
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
post:
tags:
- task
summary: タスク登録
description: タスクを登録します
requestBody:
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/Task"
- properties:
name:
type: string
example: Example Task
required:
- name
responses:
"200":
description: タスクの取得成功
content:
application/json:
schema:
$ref: "#/components/schemas/Task"
課題
- 汎用的に使用が出来るようにする。
- Supabaseとの連携性を考える
Githubのリポジトリ
このスクラップは2日前にクローズされました