AI

ZodからOpenAPIを生成する方法|zod-to-openapiとZod 4ネイティブ変換・ライブラリの選び分け

TypeScriptで書いたZodスキーマを、そのままOpenAPI(Swagger)仕様書に変換したい——それが「zod-to-openapi」で検索される課題です。Zodスキーマを単一の情報源にすれば、バリデーション・型・API仕様書を三重にメンテする必要がなくなります。ただし手段は一つではありません。Zod 4はライブラリなしでOpenAPI形式のJSON Schemaを出力できるようになり、既存の@asteasolutions/zod-to-openapizod-openapi・Honoやfastify向けの統合パッケージと、選択肢が並びます。本記事は各手段の役割と選び分け、そして「OpenAPIからZodを起こす」逆方向まで、2026年時点の版で整理します。

まとめ:ZodとOpenAPIを橋渡しする最短ルート

  • Zod 4以降なら、まず標準のz.toJSONSchema(schema,{ target:"openapi-3.0" })を試す。追加ライブラリなしでOpenAPI 3.0互換のスキーマが出る。
  • パス・レスポンスを含む完全なOpenAPIドキュメントを組むなら専用ライブラリ。汎用は@asteasolutions/zod-to-openapi(v9系)かzod-openapi(v6系)。
  • フレームワーク統合が要るなら統合版を選ぶ:Honoは@hono/zod-openapi、Fastifyはfastify-zod-openapi。ルート定義とバリデーションを兼ねられる。
  • 方向を取り違えない。「Zod→OpenAPI」は仕様書生成、「OpenAPI→Zod」は既存仕様からのスキーマ/クライアント生成(openapi-zod-client)で、目的が逆。

以下、それぞれの仕組みと使い分けの基準を具体的なコードで見ていきます。

zod-to-openapiとは:Zodスキーマを起点にAPI仕様を生成する仕組み

ZodはTypeScript向けのスキーマ定義・バリデーションライブラリで、z.object()などで宣言したスキーマから静的な型(z.infer)を同時に得られます。zod-to-openapiが担うのは、このZodスキーマをOpenAPI仕様(旧Swagger)のスキーマ定義へ変換する部分です。

スキーマを「単一の情報源」にして三重管理をやめる

Zodを使わない構成では、実行時バリデーション・TypeScriptの型・OpenAPI仕様書の三つを別々に書き、変更のたびに三箇所を揃える必要があります。ここがズレると「型は通るのに仕様書と実装が食い違う」不具合の温床になります。Zodスキーマを情報源に一本化すると、バリデーションと型はZodが、API仕様書はzod-to-openapi系が同じスキーマから導出するため、二重・三重の手直しが消えます。これが「Zodスキーマを単一の情報源(single source of truth)にする」という言い回しの実体です。

「Zod→OpenAPI」と「OpenAPI→Zod」を取り違えない

検索クエリには「zod openapi」と並んで「openapi to zod schema」「zod from openapi」も混在します。これは向きが逆の別作業です。Zod→OpenAPIは、コード側のZodスキーマからAPI仕様書を生成する流れ(本記事の主題)。OpenAPI→Zodは、既にあるOpenAPI仕様書(外部APIの定義など)からZodスキーマや型付きクライアントを起こす流れで、担当ライブラリも違います。着手前にどちらが目的かを決めておくと、選ぶパッケージを間違えません。逆方向は本記事末尾で扱います。

Zod 4のネイティブ変換:ライブラリなしでOpenAPIスキーマを出力

Zod 4で標準搭載されたz.toJSONSchema()により、これまでzod-to-json-schemaなど外部ライブラリに頼っていたJSON Schema変換がZod本体で完結します。zod-to-json-schemaは2025年11月以降アクティブなメンテナンスを終える方針が示され、Zod 4標準への移行が推奨されています。

targetとioオプション:OpenAPI 3.0を指定する

変換はz.toJSONSchema(schema, options)で行い、targetで出力するスキーマ規格を選びます。既定はdraft-2020-12で、OpenAPI 3.0向けにはopenapi-3.0を指定します(ほかにdraft-07draft-04)。

import { z } from 'zod';

const User = z.object({
  id: z.string(),
  age: z.number().int(),
});

const schema = z.toJSONSchema(User, { target: "openapi-3.0" });

ioオプションは変換対象を入力型か出力型かで切り替えます。既定はoutputで、transformなど入出力で型が変わるスキーマではio:"input"を指定してリクエスト側の形を出力します。

.meta()でdescriptionやexampleを付与する

OpenAPIのスキーマに説明や例を載せるには、Zodの.meta()でメタデータを登録します。登録した項目はそのまま生成後のスキーマへ写されます。

const email = z.string().meta({
  title: "Email address",
  description: "Your email address",
});

この.meta()方式は後述のzod-openapi(samchungy版)も採用しており、Zod標準の作法に沿うため学習コストが小さいのが利点です。

JSON Schemaで表現できない型の扱い

z.bigint()z.date()z.map()z.set()z.transform()z.symbol()などはJSON Schemaに対応表現がなく、既定(unrepresentable:"throw")では例外になります。日付を文字列として扱いたいといった場合は、unrepresentable:"any"overrideコールバックで変換を明示します。

z.toJSONSchema(z.date(), {
  unrepresentable: "any",
  override: (ctx) => {
    ctx.jsonSchema.type = "string";
    ctx.jsonSchema.format = "date-time";
  },
});

「Zodでは表現できるがJSON Schema/OpenAPIには落ちない型がある」点は、ネイティブ変換を採用する際に最初に押さえるべき制約です。

OpenAPIドキュメント生成ライブラリの選び方

単一スキーマの変換ではなく、パス・パラメータ・レスポンスを束ねた完全なOpenAPIドキュメントを組むなら、専用ライブラリが向きます。用途別に代表的な4つを整理します。

ライブラリ 最新版 peer zod 向き先 メタ付与
@asteasolutions/zod-to-openapi 9.1.0 ^4.0.0 汎用 .openapi()
zod-openapi (samchungy) 6.0.0 ^4.0.0 汎用 .meta()
@hono/zod-openapi 1.5.1 ^4.0.0 Hono統合 .openapi()
fastify-zod-openapi 5.7.0 ^3.25.74 ||^4.0.0 Fastify統合 .meta()

いずれもZod 4を前提に更新が進んでおり、Zod 3のまま使う場合は後述のとおり旧メジャーへの固定が必要です。

@asteasolutions/zod-to-openapi(汎用・registry方式)

最も広く使われてきた汎用ライブラリで、最新は9.1.0(peer zod ^4.0.0)。Zodを拡張するextendZodWithOpenApi(z)を最初に呼び、.openapi()でメタデータやコンポーネント名を付け、registryにパスを登録してからドキュメントを生成します。

import { extendZodWithOpenApi, OpenAPIRegistry, OpenApiGeneratorV3 } from '@asteasolutions/zod-to-openapi';
import { z } from 'zod';

extendZodWithOpenApi(z);
const registry = new OpenAPIRegistry();

const User = z.object({
  id: z.string().openapi({ example: '1212121' }),
  name: z.string(),
}).openapi('User');

registry.register('User', User);

const generator = new OpenApiGeneratorV3(registry.definitions);
const doc = generator.generateDocument({ openapi: '3.0.0', info: { title: 'API', version: '1.0.0' } });

OpenAPI 3.1で出力する場合はOpenApiGeneratorV31を使います。Zod 3世代のプロジェクトは7.3.4が最終対応版で、こちらは新規機能追加が止まっています。

zod-openapi(.meta()準拠・monkey patch不要)

samchungy版のzod-openapi(最新6.0.0、peer zod ^4.0.0)は、Zod標準の.meta()でメタデータを扱う点が特徴です。extendZodWithOpenApiのようなZod本体への拡張(monkey patch)が不要で、パッケージを読み込むだけで型補完まで効きます。ドキュメントはcreateDocument()にOpenAPIオブジェクトを渡して組み立てます。

import { createDocument } from 'zod-openapi';
import { z } from 'zod';

const Job = z.object({ id: z.string() }).meta({ id: 'Job' });

const document = createDocument({
  openapi: '3.1.0',
  info: { title: 'My API', version: '1.0.0' },
  paths: { },
});

Zodへの副作用を避けたい、あるいは標準の.meta()に統一したいチームに向きます。

@hono/zod-openapi(Honoのルート定義と統合)

Honoを使うなら、ルーティング・バリデーション・仕様生成を一体化した@hono/zod-openapi(最新1.5.1、peer hono >=4.10.0zod ^4.0.0)が有力です。内部で@asteasolutions/zod-to-openapi(8.5系)を利用しており、createRouteで定義したルートのスキーマがそのままOpenAPIに反映され、app.doc()で仕様書エンドポイントを公開できます。

import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi';

const app = new OpenAPIHono();
const route = createRoute({
  method: 'get',
  path: '/users/{id}',
  responses: { 200: { description: 'OK' } },
});

app.doc('/doc', { openapi: '3.0.0', info: { title: 'API', version: '1' } });

「バリデーション用のZodと仕様書用の定義を二重に書きたくない」Hono利用者にとっては、これが最短経路です。

fastify-zod-openapi(Fastify 5プラグイン)

Fastify向けはfastify-zod-openapi(最新5.7.0、peer fastify 5zod ^3.25.74 ||^4.0.0)。前述のzod-openapiをFastifyプラグインとして統合したもので、ルートスキーマにZodを使いつつOpenAPI仕様を生成します。Zod 3系も受け付ける版指定になっており、移行途中のプロジェクトでも導入しやすい構成です。

Zod 4以降、外部ライブラリは必要か:使い分けの基準

Zod 4がz.toJSONSchema()を標準搭載したことで、「ライブラリを入れるべきか」の判断が変わりました。結論から言えば、要件次第で標準機能とライブラリを使い分けるのが妥当で、常にライブラリを入れる時代ではありません。

単発のスキーマをOpenAPI互換のJSON Schemaに変換したいだけなら、z.toJSONSchema(schema,{ target:"openapi-3.0" })で十分で、依存を増やす理由はありません。一方、パス・パラメータ・レスポンス・再利用コンポーネント($ref)を含む完全な仕様書を組むなら、その構築を肩代わりする@asteasolutions/zod-to-openapizod-openapiを選ぶ価値があります。ルーティングとバリデーションを同じ定義で兼ねたいなら、HonoやFastifyの統合版が実装量を最も減らします。逆に、フレームワーク統合版をフレームワーク抜きで使うのは適しません。迷ったら「仕様書だけ欲しいのか、動くAPIの一部として欲しいのか」で切り分けると外しません。

逆方向:OpenAPIからZodスキーマと型付きクライアントを生成する

ここまでと逆に、既存のOpenAPI仕様書からZodスキーマを起こしたい場合はopenapi-zod-client(最新1.18.3)を使います。OpenAPI(Swagger)定義を入力に、Zodスキーマと、Zodiosベースの型付きAPIクライアントを生成します。外部の公開APIや別チームが定義した仕様に対して、フロントエンド側で型安全に呼び出したいケースに向きます。「zod-to-openapi」と名前は似ていますが担当は真逆で、自分のコードから仕様書を出すのか(Zod→OpenAPI)、他者の仕様から呼び出しコードを起こすのか(OpenAPI→Zod)で選ぶツールが変わる点に注意してください。

よくある質問

Zodの読み方と概要は?

Zodは「ゾッド」と読み、TypeScript向けのスキーマ宣言・バリデーションライブラリです。基本的なインストールや型推論の使い方はZodの使い方入門で解説しています。本記事はそのZodスキーマをOpenAPIへ変換する応用にあたります。

codegen(コード生成)とzod-to-openapiは何が違う?

zod-to-openapi系は「Zodスキーマ→OpenAPI仕様書」を生成します。一方、OpenAPI仕様書から各言語のサーバー/クライアントコードを生成するのはOpenAPI Generatorなどのコード生成ツールの役割です。Zodで仕様書を出し、その仕様書からコードを起こす、という順で組み合わせることもできます。

どのライブラリを選べばよい?

単発変換はZod 4標準のz.toJSONSchema、汎用の仕様書生成は@asteasolutions/zod-to-openapizod-openapi、Hono利用なら@hono/zod-openapi、Fastify利用ならfastify-zod-openapiが基準です。Zod本体への拡張を避けたい場合は.meta()準拠のzod-openapiが扱いやすいです。

Zod 3のプロジェクトでも使える?

使えますが、多くのライブラリの最新版はpeer依存がzod ^4.0.0です。Zod 3のままなら@asteasolutions/zod-to-openapiは7.3.4に固定するなど、Zod 3対応の版へバージョンを合わせる必要があります。fastify-zod-openapiのようにZod 3系を受け付ける版指定のパッケージもあります。

hono/zod-validatorとの違いは?

@hono/zod-validatorはリクエストをZodで検証するためのミドルウェアで、OpenAPI仕様書は生成しません。バリデーションに加えてOpenAPI仕様まで出したい場合は@hono/zod-openapiを選びます。仕様生成が不要なら軽量な@hono/zod-validatorで十分です。

関連記事

資料請求

RELATED POSTS 関連記事