首页
/ Strapi OpenAPI 包技术栈解析:Jest、Rollup 工具链与 Zod 到 OpenAPI 3.1 的转换实现

Strapi OpenAPI 包技术栈解析:Jest、Rollup 工具链与 Zod 到 OpenAPI 3.1 的转换实现

2026-09-04 15:50:31作者:邓越浪Henry

本文围绕 Strapi monorepo 中 @strapi/openapi 包的技术选型文档展开,逐一剖析其构建工具链(Jest / Rollup / Prettier / ESLint)与三个核心运行时依赖(Zod、Debug、类型包)的实际用法。读完本文后,你将理解 Strapi 如何通过"在路由上挂载 Zod schema、再经 z.toJSONSchema 统一转换"这一机制自动生成 OpenAPI 文档,并能定位到每个技术点在源码中的具体实现位置。

包定位与整体技术面

OpenAPI 包(package.json)的 npm 包名为 @strapi/openapi,自述为 "A tool set to help generate and validate API documentation for Strapi projects",当前版本为 5.52.2,要求 Node.js >=20.0.0 <=26.x.x

该包遵循 monorepo 的统一工具链,与 Strapi 其他包保持一致:

工具 用途 在仓库中的对应物
Jest 单元测试 jest.config.jstests
Rollup 构建 rollup.config.mjs、根目录 rollup.utils.mjs
Prettier 代码格式化 根目录 lint-staged 共享配置(如 lint-staged.shared.mjs
ESLint 代码静态检查 package.jsonlint: eslint . --max-warnings=0 脚本

值得注意的是,该包刻意保持"轻量足迹"(light footprint):dependencies 中只有三个运行时依赖——debug@4.3.4openapi-types@12.1.3zod@4.4.3。类型系统相关依赖(@strapi/types@5.52.2@types/debug@types/jest@types/nodejest@29.6.0)全部放在 devDependencies 中,不会传导给 Strapi 项目的使用者。

Jest 单元测试配置

jest.config.js 可以确认包内测试的完整配置:

  • 继承 monorepo 根目录的 jest-preset.unit.js 预设,保证各包测试行为一致;
  • 使用 @swc/jest 转换 TypeScript 源码;
  • testMatch 限定为 <rootDir>/__tests__/**/*.test.ts,即测试文件集中在 __tests__ 目录;
  • displayNameStrapi - OpenAPI,在 monorepo 聚合运行时可区分输出;
  • 显式覆写了 collectCoverageFrom——从源码注释看,这是因为根配置中的 <rootDir>/packages/** 在包级运行时会被错误解析为 packages/core/openapi/packages/**,所以这里改用 <rootDir>/src/**/*.{js,ts} 并在 CI 场景下排除 distconfig 文件等。

测试文件覆盖了包的核心能力,包括 zod-to-openapi.test.tsdocument-assemblers.test.tsoperation-assemblers.test.ts 等,与后文将讲解的 Zod 转换、Assembler 组装流程一一对应。

Rollup 构建流程

rollup.config.mjs 非常简洁,直接复用 monorepo 的 baseConfig

import { baseConfig } from '../../../rollup.utils.mjs';

export default baseConfig({
  input: './src/index.ts',
  outDir: './dist',
  rootDir: './src',
});

结合 package.jsonscripts 可以看到完整构建链:

  • build = clean --parallel build:code build:types,即清理 dist 后并行执行代码与类型声明构建;
  • build:code 运行 rollup -c,由 Rollup 产出 CJS(dist/index.js)与 ESM(dist/index.mjs)双格式;
  • build:types 运行 tsc -p tsconfig.build.json --emitDeclarationOnly,只输出 .d.ts 类型声明;
  • watch 提供开发时的 rollup -c -w 监听模式;
  • prepublishOnly 在发布前强制 clean && build,防止发布过期产物。

exports 字段同时暴露 types / source / import / require 四组条件,其中 "source": "./src/index.ts" 使得 monorepo 内部消费方在开发态可以直接引用 TypeScript 源码。

依赖之一:Zod —— 从路由 schema 到 OpenAPI 文档的核心

这是原技术文档中最重的一节。文档给出的核心机制是:路由对象在 route.requestroute.response 上携带 Zod schema,Assembler 通过 z.toJSONSchema 将这些 schema 转换为 OpenAPI 的 parameters、request body 与 response 对象,实现位于 src/utils/zod.tsComponentsWriter 后处理器。

为什么 target 固定为 draft-2020-12

zod.ts 顶部定义了全包的转换选项常量:

export const OPENAPI_SCHEMA_CONVERSION_OPTIONS = {
  target: 'draft-2020-12',
  io: 'output',
} as const satisfies Pick<z.core.RegistryToJSONSchemaParams, 'target' | 'io'>;

源码注释解释了两个取值的原因:OpenAPI 3.1 采用 JSON Schema 2020-12 方言,而 Zod 4.4.3 没有 openapi-3.1 这一 target,因此用 draft-2020-12 保持 schema 在 3.1 下的合法性;显式指定 io: 'output' 则保留了此前固化的转换方向(按输出模式而非输入模式转换)。

zodToOpenAPI:本地 registry 与 __shared 提升

核心导出函数 zodToOpenAPI(zodSchema, schemaStore, options?) 的转换流程(见 zod.ts L186-L221)可以归纳为四步:

  1. randomUUID() 生成一个临时 id,创建本地 z.registry,并把入参 schema 以该 id 注册进去;
  2. 遍历 Strapi 应用持有的 contentAPISchemaRegistry(类型来自 @strapi/typesCore.ContentAPISchemaRegistry),把其中所有命名定义也加入本地 registry——这样引用可以按名字解析,而不会落入下文要讲的 __shared 桶;
  3. 调用 z.toJSONSchema(registry, { ...OPENAPI_SCHEMA_CONVERSION_OPTIONS, uri: toComponentsPath }) 完成整体转换,toComponentsPath 会把引用 URI 统一改写为 OpenAPI 风格的 #/components/schemas/<id>
  4. 执行 liftZodSharedDefinitions 后,只返回入参 schema 对应的那一份结果,并剥离 $id 字段,保证输出文档稳定。

liftZodSharedDefinitions 解决的是一条具体的 Zod 4.4.3 行为:当某个已命名的嵌套 schema 不在转换 registry 中时,Zod 会把它暂时停在 #/components/schemas/__shared#/$defs/<id> 这个"共享桶"里。该函数把 __shared 桶中的定义逐个提升为 components.schemas 下的一等公民条目,并重写所有指向该桶的 $ref;同时通过 options.extractedComponentSchemas 这个"收获袋"把提升出的命名 schema 传给 Assembler,最终由后处理器合并进文档。

ComponentsWriter 后处理器

component-writer.ts 中的 ComponentsWriter 是文档级的收尾环节:它把整个 strapi.contentAPISchemaRegistry 注册进一个新的 Zod registry,再次执行 z.toJSONSchema 转换,经过 liftZodSharedDefinitions 清洗后,将转换结果与 Assembler 阶段收获在 context.registries.extractedComponentSchemas 中的 schema 一起,合并写入 output.data.components.schemas(已有 components 其余字段会被保留)。

两条设计取舍

原技术文档还明确了两条重要提示,均可在源码中得到印证:

  • 把 schema 留在路由上,是为了避免 OpenAPI 的领域逻辑泄漏进 Strapi core——包内所有转换只消费路由携带的 Zod 类型,不反向理解 Strapi 的业务模型;
  • 循环依赖仍是难点:模型之间的循环引用(relations、components、dynamic zones、media)在最终生成的组件 schema 中难以准确表达。这一点属于当前实现已声明的限制,阅读该包生成的文档时应对此有预期。

依赖之二:Debug 命名空间体系

Strapi 使用 debug 库输出文档生成过程的详细日志。包级实现非常小,见 src/utils/debug.ts

export const createDebugger = (
  section: string | null = null,
  namespace: string = DEBUG_NAMESPACE
) => {
  return section !== null ? createDebug(`${namespace}:${section}`) : createDebug(namespace);
};

根命名空间定义在 constants.ts 中,即 strapi:core:openapi,各组件调用 createDebugger('xxx') 时自动追加自己的后缀(如 strapi:core:openapi:xxx)。在开发或排查 OpenAPI 文档生成问题时,只需:

DEBUG=strapi:core:openapi:* node -r dotenv/config your-app.js

即可打开全量 debug 输出。该 wrapper 的价值在于调用方不需要记忆并手写完整命名空间,前缀由包统一保证一致。

依赖之三:类型系统

原技术文档列出的两组类型依赖,在 package.json 中的实际形态如下:

  • @strapi/types@5.52.2(devDependency):提供 Strapi 核心对象的类型表示,如路由(route)与应用对象。包源码中的 Core.ContentAPISchemaRegistry(见 zod.tsschemaStore 参数类型)即来自该包,对应源码目录 packages/core/types
  • openapi-types@12.1.3(runtime dependency,仅类型用途):提供完整 OpenAPI 文档及其子对象的类型。包内统一使用其 OpenAPIV3_1 命名空间,例如 zodToOpenAPI 的返回类型为 OpenAPIV3_1.SchemaObject | OpenAPIV3_1.ReferenceObjectComponentsWriter 写入 components.schemas 时也以 Record<string, OpenAPIV3_1.SchemaObject> 约束,说明该包产出的是 OpenAPI 3.1 规范的文档对象。

小结与延伸阅读

@strapi/openapi 的技术选型体现了一条清晰的主线:工具链完全复用 monorepo 标准(Jest + Rollup + Prettier + ESLint),运行时依赖收敛到三个(Zod、Debug、openapi-types),核心工程难点集中在"Zod registry 转换 → __shared 桶提升 → ComponentsWriter 合并"这条 schema 落地链路上。若要继续深入,建议按以下顺序阅读仓库内的关联文档与源码:

需要说明的是:以上版本与配置信息均以当前仓库快照(包版本 5.52.2)为准,跨版本使用时请以实际项目的 package.json 与文档为准。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341