Zod + HonoでTypeScript スキーマ定義 & OpenAPI ドキュメント作成
1. 本スキーマ定義で使うツール・ライブラリ
-
Zod
TypeScript 向けのバリデーションライブラリ
スキーマ定義を通して入力データの検証や型の推論が可能
例:z.object({...})
でオブジェクトのスキーマを定義し、z.string()
,z.date()
などのメソッドで各プロパティの型を指定する -
@hono/zod-openapi
Zod に OpenAPI 用のメタデータ(例:example
、パラメータの位置情報など)を付与する機能を追加するライブラリ
定義したスキーマから自動で OpenAPI の仕様書を生成できるようにする
例:.openapi({ example: "値" })
をプロパティにチェーンし、ドキュメント上に例示値を表示する -
OpenAPI
RESTful API の仕様を記述するための標準フォーマット
スキーマに OpenAPI 用の情報を付与することで、API ドキュメント生成ツール(Swagger など)から自動で仕様書を生成できるようにする
2. スキーマ定義の流れ
-
基本となるオブジェクトスキーマの作成
z.object({ ... })
を用いて、API のデータ構造を定義する
各プロパティにはz.string()
,z.date()
,z.string().nullable()
などで型を指定する -
OpenAPI 用メタデータの追加
.openapi({ example: "値" })
を各プロパティにチェーンし、ドキュメントに表示される例を設定する
オブジェクト全体に名前を付ける場合は.openapi("SchemaName")
をチェーンし、OpenAPI のスキーマ名として登録する -
レスポンススキーマの定義
単一オブジェクトではなく複数のオブジェクトを返す場合、z.array(...)
を用いて配列スキーマを定義する -
クエリパラメータのスキーマ定義
API のリクエストクエリパラメータもz.object({...})
で定義する
.openapi({ param: { name, in } })
を付与し、パラメータ名やパラメータがどこに存在するか(in: "query" など)を明示する
3. 細かい文法の解説
-
import { z } from "@hono/zod-openapi"
Zod の関数群を表す z をインポートする
標準の Zod に加えて、OpenAPI 用の.openapi()
メソッドが拡張されている -
z.object({ ... })
オブジェクト全体のスキーマを定義する
中括弧{ ... }
内に各プロパティ名とその型の定義を記述する -
プロパティの型定義例
-
z.string()
文字列であることを検証する -
z.string().nullable()
文字列または null を許容する (string | null) -
z.string().nullish()
文字列または null, undefined を許容する (string | null | undefined) -
z.date()
日付オブジェクトであることを検証する
-
-
.openapi({ ... }) メソッド
スキーマやプロパティに OpenAPI 用のメタデータを追加する
例:z.string().openapi({ example: "SomeExampleValue", });
ドキュメント上に
"SomeExampleValue"
というサンプル値が表示される -
.openapi("SchemaName") の使い方
スキーマ定義全体に名前を付与し、OpenAPI 仕様書のコンポーネントスキーマとして登録する
例:.openapi("GetSomethingSchema")
-
z.array(GetSomethingSchema)
配列内の各要素が特定スキーマに準拠していることを検証する -
クエリパラメータ定義での .openapi({ param: { name, in } })
クエリパラメータなどのリクエストパラメータ固有の情報を追加する
例:z.string().openapi({ param: { name: "someQueryParam", in: "query", }, example: "SomeValue", });
someQueryParam
というパラメータがクエリ文字列に存在し、サンプル値がドキュメントに反映される
4. 定義例
以下のような型がレスポンスとして返ってくることを想定するとする
export type SomethingProps = {
kind: string;
docId: string;
containerId: string;
resourceId: string;
resourceName: string;
routeId: string;
routeName: string;
versionId: string | null;
versionName: string | null;
datePublished: string | null;
imageUrl: string;
createdTime: Date | null;
updatedTime: Date | null;
};
実際に作成したスキーマ例
import { z } from "@hono/zod-openapi";
import { SomeOtherSchema } from "./some-other";
export const GetSomethingSchema = z
.object({
kind: z.string().openapi({ example: "Kind" }),
docId: z.string().nullable().openapi({ example: "DocId" }),
containerId: z.string().nullable().openapi({ example: "ContainerId" }),
resourceId: z.string().openapi({ example: "ResourceId" }),
resourceName: z.string().openapi({ example: "ResourceName" }),
routeId: z.string().nullable().openapi({ example: "RouteId" }),
routeName: z.string().openapi({ example: "RouteName" }),
versionId: z.string().nullable().openapi({ example: "VersionId" }),
versionName: z.string().nullable().openapi({ example: "VersionName" }),
datePublished: z.string().nullable().openapi({ example: "DatePublished" }),
imageUrl: z.string().nullable().openapi({ example: "ImageUrl" }),
createdTime: z.date().nullable().openapi({ example: "CreatedTime" }),
updatedTime: z.date().nullable().openapi({ example: "UpdatedTime" }),
})
.openapi("GetSomethingSchema");
export const GetSomethingResponseSchema = z
.array(GetSomethingSchema)
.openapi("GetSomethingResponseSchema");
export const GetSomethingQueryParamsSchema = z.object({
resourceName: z.string().openapi({
param: {
name: "resourceName",
in: "query",
},
example: "サンプルリソース名",
}),
routeId: z.string().openapi({
param: {
name: "routeId",
in: "query",
},
example: "サンプルルート",
}),
});
GetSomethingSchema, GetSomethingResponseSchema, GetSomethingQueryParamsSchema の役割
-
GetSomethingSchema
単一のリソースを表すスキーマ
各プロパティに対するバリデーションやサンプル値の指定を行う
内部処理やレスポンス生成時に、このスキーマに適合しているかを検証する -
GetSomethingResponseSchema
API のレスポンス全体を表すスキーマ
z.array(GetSomethingSchema)
として定義され、複数のリソースオブジェクト(配列)を返すことを示す
配列形式であり、各要素がGetSomethingSchema
に従っていることを保証する -
GetSomethingQueryParamsSchema
エンドポイントに渡されるクエリパラメータを定義するスキーマ
例ではresourceName
,routeId
などを定義し、OpenAPI のメタデータ(パラメータ名、位置、サンプル値)を付与している
クエリパラメータの検証と自動ドキュメント生成が可能になる
以上です。
Discussion