Strapi OpenAPI 包实战指南:用 generate() API 与 CLI 一键生成 OpenAPI 规范文档
Strapi 内置的 @strapi/openapi 包提供了将 Strapi 应用注册路由转换为 OpenAPI 规范文档的工具集,是社区 SDK 生成、Swagger 支持等需求的基础。本文围绕官方 Usage 文档展开,讲清 generate() 的函数签名、参数与返回值,以及实验性 CLI 命令 strapi openapi generate 的实际行为,并结合仓库源码剖析"路由收集 → 上下文构建 → 装配 → 后处理"的生成管线,帮助你在插件或脚本中正确产出 OpenAPI JSON。
一、generate():从 Strapi 应用实例生成 OpenAPI 文档
generate 是 @strapi/openapi 包对外暴露的核心函数,作用是"基于给定的 Strapi 应用生成一份 OpenAPI JSON 文档"。默认情况下,它会收集应用中注册的 content API 路由,将它们转换为 OpenAPI 的 path 对象,并填充其余 OpenAPI components。
函数签名
function generate(strapi: Core.Strapi, options?: GenerationOptions): GeneratorOutput;
strapi— 要为其生成 OpenAPI 规范的 Strapi 应用实例(Core.Strapi类型,来自@strapi/types);options.type— 要文档化的路由集合,可选:'content-api'(默认)或'admin'。
这与源码中的 JSDoc 完全一致,见 exports.ts。options 的类型定义在 types.ts:
export interface GenerationOptions {
type: 'admin' | 'content-api';
}
值得注意的是,type 字段并非可选——GenerationOptions 接口本身声明其为必填,但 generate() 的第二个参数整体是可选的(options?: GenerationOptions),内部通过 const { type = 'content-api' } = options ?? {} 兜底为默认值。
返回值
generate() 返回一个生成输出对象,包含两个字段:
| 字段 | 含义 |
|---|---|
document |
生成的 OpenAPI 规范(JSON 对象,OpenAPIV3_1.Document 类型) |
durationMs |
生成耗时(毫秒) |
从 generator.ts 的 generate() 方法可以看到,返回值直接取自上下文的 output.data 与 stats.time.elapsedTime:
return { document: data as OpenAPIV3_1.Document, durationMs: stats.time.elapsedTime };
生成计时由内置的 Timer 工具在 _bootstrap 中启动、_finalize 中停止,因此 durationMs 覆盖的是"预处理 + 装配 + 后处理"整条管线的耗时。
使用示例
import { generate } from '@strapi/openapi';
const { document, durationMs } = generate(strapi, { type: 'content-api' });
调用方拿到的 document 是内存中的完整 OpenAPI 3.1 对象,包本身不写文件(这一点在 00-intro.md 的 Scope 一节中也被明确列为"不是独立文件写入器")——落盘要么交给 CLI 包装器,要么由调用方自行处理。
二、生成管线:generate() 内部到底做了什么
从源码结构看,generate() 并不是一个简单的一拍即合函数,而是组装了一条四段式管线,这也是理解其行为与可扩展性的关键。在 exports.ts 中,它按以下顺序构建依赖:
- 处理器工厂:
PreProcessorFactory、DocumentAssemblerFactory、PostProcessorsFactory各自createAll(),生成默认的预处理、装配、后处理器集合; - 路由收集器:
RouteCollector接收三个路由提供者和一个RouteMatcher:
const routeCollector = new RouteCollector(
[
new AdminRoutesProvider(strapi),
new ApiRoutesProvider(strapi),
new PluginRoutesProvider(strapi),
],
new RouteMatcher([
// Only match content-api routes
rules.isOfType(type),
])
);
路由来源分三类:Admin 路由(AdminRoutesProvider)、Content API 路由(ApiRoutesProvider)、插件路由(PluginRoutesProvider),对应 src/routes/providers/ 下的实现;RouteMatcher 则根据 type 选项(rules.isOfType(type))过滤出匹配的路由集合。
- 生成器:将以上依赖注入
OpenAPIGenerator,执行generator.generate()。
OpenAPIGenerator.generate() 的执行顺序在 generator.ts 中清晰可见:
const context = this._initContext(this._strapi); // 1. 收集路由 + 构建文档上下文
this
._bootstrap(context) // 2. 重置并启动计时器
._preProcess(context) // 3. 依次运行注册的 pre-processors
._assemble(context) // 4. 依次运行注册的 section assemblers
._postProcess(context) // 5. 依次运行注册的 post-processors
._finalize(context); // 6. 停止计时,写入 stats
_initContext先调用routeCollector.collect()拉取匹配路由,再由DocumentContextFactory创建文档生成上下文;_assemble阶段执行的是"文档装配器",负责 info、server、security、paths(path → path-item → operation → parameters/responses/body/operation-id/tags)等 OpenAPI 章节的填充,具体实现分布在 src/assemblers/document/ 与 src/assemblers/document/path/ 中;_postProcess阶段包含 component writer 等后处理器,见 src/post-processor/。
OpenAPIGenerator 的构造函数同时暴露了 preProcessors、assemblers、postProcessors 三类可配置项(见 generator.ts),这意味着高级用法可以在不改动包源码的前提下注入自定义装配器——这与 00-intro.md 中"通过 providers、matchers、assemblers、processors 定制生成过程"的定位相符。不过官方 Usage 文档明确提示当前版本定制能力有限,后续版本会扩展。
三、CLI:strapi openapi generate
除了编程式 API,@strapi/strapi 还内置了一个实验性 CLI 包装器:
strapi openapi generate [-o, --output <path>]
-o, --output <path>:指定 OpenAPI 规范文件的输出路径。
命令注册逻辑见 packages/core/strapi/src/cli/commands/openapi/index.ts:openapi 主命令描述为"Manage OpenAPI specifications for your Strapi application",generate 子命令的 --output 选项描述为"Output file path for the OpenAPI specification"。
默认行为
不传 --output 时,结果写入项目根目录下的 specification.json。这与 generate.ts 的默认值一致:
const DEFAULT_OUTPUT = path.join(process.cwd(), 'specification.json');
命令内部流程
阅读 generate.ts 的 action 实现,完整的执行链是:
- 打印实验性警告:命令一执行就会
console.warn一段黄色警告,声明 OpenAPI 生成功能处于实验阶段,行为与输出可能在不遵循 semver 的情况下变化; - 加载应用:
createStrapiApp()先compileStrapi()再createStrapi(appContext),将日志级别强制设为error(避免污染输出),然后await app.load()加载内部模块,最后调用app.server.mount()确保路由在生成前已挂载——这是生成结果能覆盖全部已注册路由的前提; - 调用 generate():固定以
{ type: 'content-api' }调用openapi.generate(app, ...)(当前 CLI 不暴露 admin 选项); - 写文件:
fse.outputFileSync(filePath, JSON.stringify(document, null, 2)),即以 2 空格缩进的格式化 JSON 写入目标路径; - 输出摘要:从
app.config.get('info')读取应用名与版本号,打印形如Generated an OpenAPI specification for "AppName vX.Y.Z" at specification.json in 123ms的加粗汇总行; - 清理:
await app.destroy()销毁应用实例。
实验性声明:OpenAPI 生成功能是实验性的(experimental),其行为和输出可能在后续版本中变化且不受 semver 约束——文档与 CLI 启动警告两处均强调了这一点。
四、生成结果的边界与已知限制
结合 00-intro.md 的 Scope 说明与源码,使用 generate() / CLI 时有几点需要心里有数:
- 仅针对 Strapi 应用:该包的设计目标是"专门为 Strapi 应用定制地"生成 OpenAPI 文档,不是面向非 Strapi 应用的通用 OpenAPI 生成器,也不包含 Swagger UI(那是旧版 documentation 插件的能力,本包不直接替代它);
- 循环引用尚未完整表达:模型之间的循环引用(relations、components、dynamic zones、media)在组件 schema 中还没有被完整表示;
- 版本目标:生成的文档遵循 OpenAPI 3.1(源码中
document被断言为OpenAPIV3_1.Document,见 generator.ts)。
五、测试与验证路径
如果你想核对生成行为,仓库内配套了完整的单元测试,覆盖路由收集、匹配规则、装配器与 zod→OpenAPI 转换:
- 路由收集与匹配:collector.test.ts、route-matcher.test.ts、api-routes-provider.test.ts、plugins-route-provider.test.ts;
- 文档/操作装配器:document-assemblers.test.ts、operation-assemblers.test.ts、query-param-styles.test.ts;
- 类型转换:zod-to-openapi.test.ts。
测试用 Strapi 应用 mock 位于 strapi.mock.ts,路由 fixture 在 routes.ts,可作为构造 generate() 入参的参考样例。
小结
@strapi/openapi 的使用路径很简单:拿到已加载并挂载路由的 Strapi 实例,调用 generate(strapi, { type }) 得到内存中的 OpenAPI 3.1 文档与耗时;需要落盘时,直接用 strapi openapi generate -o <path>(默认输出 specification.json)。底层是一条"路由收集(Admin/API/Plugin 三来源 + 类型匹配)→ 上下文构建 → 预处理/装配/后处理"的管线,各环节均通过工厂注入,为后续扩展预留了空间。由于功能标记为实验性,生产使用建议关注输出结构变化并在版本升级时重新核对规范文件。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00