最近在使用 Fastify 和 @fastify/swagger 为项目生成 OpenAPI 文档时,遇到一个奇怪的问题:明明每个路由都写好了 schema,但访问 /swagger-ui 时页面却提示 No operations defined in spec!,文档内容完全是空的。
发现问题
我按照官方文档的示例,先注册了 Swagger 插件,然后注册路由,启动服务。代码大致如下:
1 | const fastify = require('fastify')(); |
但无论怎么调整配置,文档页面始终是空的,查原始JSON 中的 paths 对象也为 {}。我检查了插件顺序、schema 格式、路由注册时机,甚至写了一个最小化示例,问题依旧。
处理问题
我尝试了所有常见排查手段:
确认插件注册顺序 – 确保 Swagger 在路由之前注册。
检查
schema格式 – 确认response字段完整,且tags、description等无缺失。调整配置选项 – 补充
openapi.info中的必要字段,开启exposeRoute。查看原始 JSON – 访问
/swagger-ui/json,确认paths为空。写一个简化版本 – 仅保留 Swagger 和一个简单接口,但结果依然失败。
这些步骤都没能解决问题,文档依然空空如也。
找到原因
在反复测试简化示例时,我忽然意识到:fastify.register 是一个异步方法,它会返回一个 Promise。如果我不等待它完成就直接注册路由,那么 Swagger 插件的初始化可能尚未结束,导致后续路由无法被正确捕获。
于是我将代码修改为:
1 | await fastify.register(require('@fastify/swagger'), options); |
再次运行,文档终于正常显示了!原来正是缺少了 await 导致的问题。
总结
根本原因:@fastify/swagger 插件在注册时需要进行一些异步初始化(如构建内部状态),如果注册后立即注册路由而不等待插件完成,这些路由的 schema 信息就无法被插件收集,最终生成的文档中 paths 为空。
解决方案:
在注册 Swagger 插件时,务必加上 await(或在回调中注册路由):
1 | // 正确方式 |
如果你使用了 fastify-plugin 或模块化路由,也要确保外层已经 await 了 Swagger 的注册。
预防建议:
牢记
register是异步的,尤其是在插件有初始化逻辑时。如果不想使用
await,可以在register的回调中注册路由,但这样会破坏代码的线性结构,推荐直接使用await。养成编写最小化测试用例的习惯,能更快定位问题。
希望这篇文章能帮助遇到同样问题的开发者少走弯路!