@cabloy/zod-to-openapi

中文文档
8.1.6发布于 2026/04/16MITTypeScriptESM + CJS近 30 天下载 7382

从 Zod 计划构建 OpenAPI 计划

中文使用文档
文档目录19 节
  1. 目的和简单的例子
  2. 使用量
  3. 安装
  4. 这个 openapi 方法
  5. 基本想法
  6. 例1: 使用 tsx 调用 openapi 扩展
  7. 例2 - 需要语法
  8. 书记官处
  9. 发电机
  10. 定义计划
  11. 定义路由( W)
  12. 定义自定义组件
  13. 一个完整的例子
  14. 使用计划对注册
  15. 生成选项
  16. Zod 计划类型
  17. 支持类型
  18. 不支持的类型
  19. 技术

Zod 到 OpenAPI

npm version npm downloads

[大 对于Zod v3支持,请使用v7.3.4版本. 然而,我们并不打算积极支持这一版本 安装方式 : npm install @asteasolutions/[email protected]

一个使用 zod 计划 生成 Open API Swagger 文档.

  1. 目的和简单的例子
  2. 使用量
    1. 安装
    2. 这个 openapi 方法
    3. 书记官处
    4. 发电机
    5. 定义计划
    6. 定义路由( W)
    7. 定义自定义组件
    8. 一个完整的例子
    9. 把它加进你的建筑
    10. 使用计划对注册
    11. 生成选项
  3. Zod 计划类型
    1. 支持类型
    2. 不支持的类型
  4. 技术

我们保留一个更衣日志 作为的一部分 GitHub 发布。 。 。 .

目的和简单的例子

我们到了 Astea 解决方案 因为我们使用 佐德语 用于在我们的 API 中验证, 并厌倦了重复, 也支持一个必须同步的单独的 OpenAPI 定义 。 使用 zod-to-openapi,我们直接从我们的zod schemas生成OpenAPI定义,从而拥有单一的真理来源.

简而言之,它会变成这样:

const UserSchema = z
  .object({
    id: z.string().openapi({ example: '1212121' }),
    name: z.string().openapi({ example: 'John Doe' }),
    age: z.number().openapi({ example: 42 }),
  })
  .openapi('User');

registry.registerPath({
  method: 'get',
  path: '/users/{id}',
  summary: 'Get a single user',
  request: {
    params: z.object({ id: z.string() }),
  },

  responses: {
    200: {
      description: 'Object with user data.',
      content: {
        'application/json': {
          schema: UserSchema,
        },
      },
    },
  },
});

改为:

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          example: '1212121'
        name:
          type: string
          example: John Doe
        age:
          type: number
          example: 42
      required:
        - id
        - name
        - age

/users/{id}:
  get:
    summary: Get a single user
    parameters:
      - in: path
        name: id
        schema:
          type: string
        required: true
    responses:
      '200':
        description: Object with user data
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'

还可以使用 UserSchema 编辑 request.params 对象以校验您的 API 输入.

使用量

安装

npm install @asteasolutions/zod-to-openapi
# or
yarn add @asteasolutions/zod-to-openapi

这个 openapi 方法

为了保持Openapi定义的自然性,我们增加了一个 openapi 用于所有 Zod 对象的方法。 它的想法是提供一个方便的方法来提供OpenApi特定的数据. 它有三个超载:

  1. .openapi({ [key]: value }) - 这样我们可以指定任何 OpenApi 字段 。 举个例子 z.number().openapi({ example: 3 }) 将添加 example: 3 {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}到生成的图案.
  2. .openapi("<schema-name>") - 这样,我们规定基本的zod计划应该“注册”,即加入 components/schemas 提供 <schema-name>
  3. .openapi("<schema-name>", { [key]: value }) - 将上面的两种情况结合起来,以便我们可同时说明登记情况 <schema-name> 和其他元数据

为了成功,你需要打电话 extendZodWithOpenApi 一次在你的项目.

这只应在您的项目的共入点文件中进行一次(例如: index.ts编号 :app.ts) (中文(简体) ). 如果你用Webpack来刮树 把文件标记为有副作用.

在您的密码库中实现这一点可能有点棘手,因为 需求 是同步和 导入 是一个aync.

使用 Zod 的 .meta 软件

从v8(和zod v4)开始,您也可以使用zod的.meta来提供元数据,我们将相应读取.

有了Zod生成 JSON 计划和维护注册的新选项 我们为所有元数据信息添加了相当无缝的支持 .meta 调用,好像元数据输入 .openapi。 。 。 .

因此,以下两个计划产生完全相同的结果:

const schema = z
  .string()
  .openapi('Schema', { description: 'Name of the user', example: 'Test' });

const schema2 = z
  .string()
  .meta({ id: 'Schema2', description: 'Name of the user', example: 'Test' });

说明: 这也意味着,除非你使用一些我们更复杂的情景, 你甚至可以生成一个计划 不使用 extendZodWithOpenApi 在您的密码库中只依赖 .meta 以提供额外的元数据信息和计划名称(使用 id (b) 财产).

需要使用的情况 extendZodWithOpenApi 财务报告和审定财务报表 .openapi

  1. 当扩展既注册又希望扩展的系统 anyOf 即:
const schema = z.object({ name: z.string() }).openapi('Schema');

const schema2 = schema.extend({ age: z.number() }).openapi('Schema2'); // this one would have anyOf and a reference to the first one
  1. 定义参数元数据. 例如,在做的时候:
registry.registerPath({
  // ...
  request: {
    query: z.object({
      name: z.string().openapi({
        description: 'Schema level description',
        param: { description: 'Param level description' },
      }),
    }),
  },
});

其结果是:

  "parameters": [
      {
        "schema": {
          "type": "string",
          "description": "Schema level description" // comes directly from description
        },
        "required": true,
        "description": "Param level description", // comes from param.description
        "name": "name",
        "in": "query"
      }
  ],

基本想法

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

extendZodWithOpenApi(z);

// We can now use `.openapi()` to specify OpenAPI metadata
z.string().openapi({ description: 'Some string' });

例1: 使用 tsx 调用 openapi 扩展

//zod-extend.ts

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

extendZodWithOpenApi(z);

// package.json

  "scripts": {
    "start": "tsx --import ./zod-extend.ts ./index.ts",

例2 - 需要语法

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

extendZodWithOpenApi(z);

const { startServer } = require('./server/start');
startServer();

书记官处

这个 OpenAPIRegistry 用于收集定义的有用工具,这些定义随后将转给 OpenApiGeneratorV3 或者说 OpenApiGeneratorV31 举个例子.

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

const registry = new OpenAPIRegistry();

// Register definitions here

const generator = new OpenApiGeneratorV3(registry.definitions);

return generator.generateComponents();

发电机

有两个发电机可以使用 - OpenApiGeneratorV3 财务报告和审定财务报表 OpenApiGeneratorV31。它们共享相同的界面,但内部生成计划,以正确遵循特定开放 API 版本的数据格式 - 3.0.x 或者说 3.1.x。开放 API 版本会影响某些组件的生成方式.

例如: 将生成器从 OpenApiGeneratorV3 改为 OpenApiGeneratorV31 将导致以下差异:

z.string().nullable().openapi({refId: 'name'});
# 3.1.0
# nullable is invalid in 3.1.0 but type arrays are invalid in previous versions
name:
  type:
    - 'string'
    - 'null'

# 3.0.0
name:
  type: 'string'
  nullable: true

两种生成器在其构造器中都采用单一的参数——一系列定义——即来自登记册或普通的zod计划.

两台发电机的公用方法如下:

generateComponents 将只生成 /components openAPI 文档的章节(例如 schemas 财务报告和审定财务报表 parameters),没有生成实际路线.

generateDocument 将生成整个 OpenAPI 文档.

定义计划

OpenApi 计划应通过使用 .openapi 方法和提供名称:

const UserSchema = z
  .object({
    id: z.string().openapi({ example: '1212121' }),
    name: z.string().openapi({ example: 'John Doe' }),
    age: z.number().openapi({ example: 42 }),
  })
  .openapi('User');

const generator = new OpenApiGeneratorV3([UserSchema]);

通过使用 register 方法 OpenAPIRegistry 举个例子 要多检查一下 "使用计划与注册" 节

const UserSchema = registry.register(
  'User',
  z.object({
    id: z.string().openapi({ example: '1212121' }),
    name: z.string().openapi({ example: 'John Doe' }),
    age: z.number().openapi({ example: 42 }),
  })
);

const generator = new OpenApiGeneratorV3(registry.definitions);

如果现在跑, generator.generateComponents() 将生成以下结构:

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: string
          example: '1212121'
        name:
          type: string
          example: John Doe
        age:
          type: number
          example: 42
      required:
        - id
        - name
        - age

输出中计划的关键是第一个传递给 .openapi 方法(或 .register- 在这种情况下: User。 。 。 .

请注意: generateComponents 不返回 YAML 而是 JS 对象 - 您可以根据您的使用大小写将对象序列化为 YAML 或 JSON .

然后可以通过使用 $ref: #/components/schemas/User 在现有的 OpenAPI JSON 中。 对于通过登记处定义的路由,将自动做到这一点.

默认注意 Zod 对象将导致 "additionalProperties": true 除非使用 strict 或者说 catchall,这与Zod对象的正常使用形成对比 zod.parse 已使用.

定义路由( W)

正在注册路径或网路

使用 OpenAPI 路径注册 registerPath 方法 OpenAPIRegistry 举个例子 使用 : registerWebhook 方法并采用与 registerPath。 。 。 .

registry.registerPath({
  method: 'get',
  path: '/users/{id}',
  description: 'Get user data by its id',
  summary: 'Get a single user',
  request: {
    params: z.object({
      id: z.string().openapi({ example: '1212121' }),
    }),
  },
  responses: {
    200: {
      description: 'Object with user data.',
      content: {
        'application/json': {
          schema: UserSchema,
        },
      },
    },
    204: {
      description: 'No content - successful operation',
    },
  },
});

以上计划中的YAML相当于:

'/users/{id}':
  get:
    description: Get user data by its id
    summary: Get a single user
    parameters:
      - in: path
        name: id
        schema:
          type: string
          example: '1212121'
        required: true
    responses:
      '200':
        description: Object with user data.
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/User'
      '204':
        description: No content - successful operation

库中用于 registerPath 为: method, (中文(简体) ), path, (中文(简体) ), request 财务报告和审定财务报表 responses。其他一切直接附在路径定义上.

  • method - 一个 get, (中文(简体) ), post, (中文(简体) ), put, (中文(简体) ), delete 财务报告和审定财务报表 patch· ;
  • path - 弦 -- -- 是终点的路径;
  • request - 可选对象 body, (中文(简体) ), params, (中文(简体) ), query 财务报告和审定财务报表 headers 键,
    • query, (中文(简体) ), params - 作为 ZodObject
    • body - 带有 description (单位:千美元) content 记录其中:
      • 键为 a mediaType 字符串喜欢 application/json
      • 对象为 schema 编号 zod 类型
    • headers - 实例 ZodObject 或任意数组 zod 实例
  • responses - 键是状态代码或 default 对象为 description (单位:千美元) content 记录其中:
    • 键为 a mediaType 字符串喜欢 application/json
    • 对象为 schema 编号 zod 类型

定义路由参数

如果您不想插入所有参数定义, 您可以用 registerParameter 然后引用他们:

const UserIdParam = registry.registerParameter(
  'UserId',
  z.string().openapi({
    param: {
      name: 'id',
      in: 'path',
    },
    example: '1212121',
  })
);

registry.registerPath({
  ...
  request: {
    params: z.object({
      id: UserIdParam
    }),
  },
  responses: ...
});

YAML 相当于:

components:
  parameters:
    UserId:
      in: path
      name: id
      schema:
        type: string
        example: '1212121'
      required: true

'/users/{id}':
  get:
    ...
    parameters:
      - $ref: '#/components/parameters/UserId'
    responses: ...

注意:为了定义适用于参数本身的属性,请使用 param 财产 .openapi。任何在 param 将适用于此参数的计划.

生成完整文档

可用 : generateDocument 方法 OpenApiGeneratorV3 或者说 OpenApiGeneratorV31 举个例子 需要一个参数 - 文件配置 。 也许看起来是这样的:

return generator.generateDocument({
  openapi: '3.0.0',
  info: {
    version: '1.0.0',
    title: 'My API',
    description: 'This is the API',
  },
  servers: [{ url: 'v1' }],
});

定义自定义组件

您可以定义非 OpenAPI 计划的内容, 包括安全方案、 响应头等 。 见 此测试文件 用于实例.

一个完整的例子

可找到完整的示例代码 这里和YAML对其结果的表述 -- 这里

使用计划对注册

Schemas在引用时会自动被注册. 这意味着如果你有这样的计划:

const schema = z.object({ key: z.string().openapi('Test') }).openapi('Object');

将产生以下结构:

components:
  schemas:
    Test:
      type: 'string',
    Object:
      type: 'object',
      properties:
        key:
          $ref: '#/components/schemas/Test'
      required: ['key']

这不需要使用 OpenAPIRegistry 举个例子.

然而,以下代码可以实现同样的输出:

const registry = new OpenAPIRegistry();
const schema = registry.register(
  'Object',
  z.object({ key: z.string().openapi('Test') })
);

二、《公约》的主要利益 .registry 方法就是,您可以将登记册作为“收集”工具,将所有这些计划放入其中.

与 .openapi数字 :

// file1.ts
export const Schema1 = ...

// file2.ts
export const Schema2 = ...

new OpenApiGeneratorV3([Schema1, Schema2])

添加一个 NewSchema 输入 file3.ts 需要你手动将这个计划传递到生成器的阵列中. 注:如果 NewSchema 以任何其他计划或路径/网路定义作为参考,该定义仍将出现在所产生的文件中.

与 registry.register数字 :

// registry.ts
export const registry = new OpenAPIRegistry()

// file1.ts
export const Schema1 = registry.register(...)

// file2.ts
export const Schema2 = registry.register(...)

new OpenApiGeneratorV3(registry.definitions)

添加一个 NewSchema 输入 file3.ts 并使用 registry.register 不需要您修改生成器构造器 .

结论

使用一个 OpenAPIRegistry 如果您想要您生成的文档包含未引用的策略,则实例将非常有用. 这有时是有用的,例如,当你正在慢慢地将已有的文档与 @asteasolutions/zod-to-openapi 你们一次移出一小块 然后可以直接取自现有文件.

把它加进你的建筑

在项目中的文件中,您可以拥有这样的文件 :

export const registry = new OpenAPIRegistry();

export function generateOpenAPI() {
  const config = {...}; // your config comes here

  return new OpenApiGeneratorV3(registry.definitions).generateDocument(config);
}

然后使用导出 registry 对象将酌情登记所有计划、参数和路线.

然后可以创建一个执行导出的脚本 generateOpenAPI 函数。 此脚本可以作为您构建步骤的一部分执行, 以便它能够将结果写入一些文件, 如 openapi-docs.json。 。 。 .

生成选项

在某些情况下可以改变Schema生成. 可通过下列两种方式做到这一点:

通过一个全局配置作为生成器的第二个参数 :

const generator = new OpenApiGeneratorV3(registry.definitions, options);

目前支持的全球选项有:

const options = {
  unionPreferredType: 'oneOf' | 'anyOf' // configures whether oneOf or anyOf is used when generating a schema for a zod union
  sortComponents?: 'alphabetically'; // if sortComponents is passed with the value 'alphabetically' it would sort all schemas and parameters.
                                     // If not - they would appear in the order they were defined
}

一次性使用单一方案通过选项:

// Note it is valid for metadata to be undefined in both of the below cases:

schema.openapi('Schema', metadata, options); // when registering a schema or
schema.openapi(metadata, options) // when simply adding some metadata to it

目前支持的一次性选项有:

const options = {
  unionPreferredType: 'oneOf' | 'anyOf' // configures whether oneOf or anyOf is used when generating a schema for a zod union
}

Zod 计划类型

支持类型

目前所有支持类型列表为:

  • ZodAny

  • ZodArray

  • ZodBigInt

  • ZodBoolean

  • ZodDate

  • ZodDefault

  • ZodPrefault

  • ZodDiscriminatedUnion

    • 包括 discriminator 联盟中所有 Zod 对象注册时的映射 .register() 或载有 refId。 。 。 .
  • ZodEffects

  • ZodEnum

  • ZodIntersection

  • ZodLiteral

  • ZodNativeEnum

  • ZodNullable

  • ZodNumber

    • 包括 z.number().int() 推定为 type: 'integer'
  • ZodObject

    • 包括 .catchall 产生相应的 additionalProperties 计划
    • 还包括: strict 产生相应的 additionalProperties 计划
  • ZodOptional

  • ZodPipeline

  • ZodReadonly

  • ZodRecord

  • ZodString

    • 添加 format 用于:

      • .emoji()
      • .cuid()
      • .cuid2()
      • .ulid()
      • .ip()
      • .date()
      • .datetime()
      • .uuid()
      • .email()
      • .url()
    • 添加 pattern 页:1 .regex() 也获得支持

      ${'emoji'} ${z. string (. emoji ()}} ${emoji'} ${emoji'} ${ emoji'} ${'cuid'}} ${z. string (.cuid ()} ${'cuid'} ${'cuid'} ${"cuid"} ${'cuid2'}} ${z. string (.cuid2()}} ${'cuid2'} ${'cuid2'} ${'ulid'} ${z. string (. ulid ()}} ${'ulid'} ${"ulid"} ${'ip'} ${z. string (. ip ()} ${'ip'}

  • ZodTuple

  • ZodUnion

  • ZodUnknown

扩展实例 ZodObject 并导致 OpenApi 定义 allOf

不支持的类型

如果您试图从一个不是上述类型之一的zod schema创建 OpenAPI 计划,那么您将收到一个 UnknownZodTypeError。 。 。 .

您仍可自行注册此类计划,提供 type 通过 .openapi 方法。 如果你觉得理想的行为可以自动实现 毫不犹豫地通过Github Issues 来描述你的案子.

已知问题

  1. z.nullable(schema) 不为基本注册计划生成一美元ref。 。 。 .
  • 这是一个执行方面的限制.
  • 不过,你可以简单地使用 schema.nullable() 效果完全一样 zod 明智,但我们也完全支持.

技术

版本与发布记录

@cabloy/zod-to-openapi 共发布 7 个版本,首次发布于 2025/09/12;过去 12 个月发布了 2 个版本。

版本发布日期距今
8.1.6latest2026/04/166 个月前
8.1.52025/12/1910 个月前
8.1.42025/10/111.1 年前
8.1.32025/10/111.1 年前
8.1.22025/10/111.1 年前
8.1.12025/09/121.2 年前

关于 @cabloy/zod-to-openapi 的常见问题

@cabloy/zod-to-openapi 是什么?

从 Zod 计划构建 OpenAPI 计划

如何安装 @cabloy/zod-to-openapi?

在项目目录运行 npm install @cabloy/zod-to-openapi,或使用 pnpm add / yarn add / bun add。

@cabloy/zod-to-openapi 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@cabloy/zod-to-openapi 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@cabloy/zod-to-openapi 可以商用吗?

@cabloy/zod-to-openapi 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@cabloy/zod-to-openapi 的下载量有多少?

根据 npm 官方下载统计,@cabloy/zod-to-openapi 在 2026/10/09 – 2026/11/07 这 30 天内被下载 7,382 次,日均约 246 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@cabloy/zod-to-openapi 还在维护吗?

最新版本 8.1.6 发布于 2026/04/16(6 个月前);至今共发布 7 个版本,其中过去 12 个月发布了 2 个;首次发布于 2025/09/12。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。