🎃

Zod + HonoでTypeScript スキーマ定義 & OpenAPI ドキュメント作成

2025/02/05に公開

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. スキーマ定義の流れ

  1. 基本となるオブジェクトスキーマの作成
    z.object({ ... }) を用いて、API のデータ構造を定義する
    各プロパティには z.string(), z.date(), z.string().nullable() などで型を指定する

  2. OpenAPI 用メタデータの追加
    .openapi({ example: "値" }) を各プロパティにチェーンし、ドキュメントに表示される例を設定する
    オブジェクト全体に名前を付ける場合は .openapi("SchemaName") をチェーンし、OpenAPI のスキーマ名として登録する

  3. レスポンススキーマの定義
    単一オブジェクトではなく複数のオブジェクトを返す場合、z.array(...) を用いて配列スキーマを定義する

  4. クエリパラメータのスキーマ定義
    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 のメタデータ(パラメータ名、位置、サンプル値)を付与している
    クエリパラメータの検証と自動ドキュメント生成が可能になる

 
 
 
 
以上です。

KA projects

Discussion