尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Scalar Fastify 集成实战:用 @scalar/fastify-api-reference 从 OpenAPI 文档一键渲染交互式 API Reference

Scalar Fastify 集成实战:用 @scalar/fastify-api-reference 从 OpenAPI 文档一键渲染交互式 API Reference Scalar Fastify 集成实战用 scalar/fastify-api-reference 从 OpenAPI 文档一键渲染交互式 API Reference【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar本文基于 Scalar 仓库中的 Fastify 集成文档 与对应插件源码 integrations/fastify/src/fastifyApiReference.ts 撰写。它介绍如何通过scalar/fastify-api-reference插件在你的 Fastify 应用中以几行代码渲染出基于 OpenAPI/Swagger 文档的交互式 API Reference并深入讲清文档来源优先级、OpenAPI 端点再暴露、CSP nonce、日志与鉴权钩子等每一项插件选项的底层实现帮助你既会配置也懂其原理。安装与注册scalar/fastify-api-reference是一个标准的 Fastify 插件源码中通过fastify-plugin包装见 fastifyApiReference.ts安装后直接register即可npm install scalar/fastify-api-referenceawait fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, })注意该插件同时兼容 Fastify v4 与 v5并且以 ES Module 形式发布因此需要使用动态import()如上所示或在 ESM 项目中通过顶层import注册。从源码的fastify-plugin元信息看其声明的兼容范围为fastify: 4.x || 5.x || 6.xFastify 会在注册时校验该范围并在不兼容时快速失败见 fastifyApiReference.ts#L262-L267。当前版本要求 Node.js20见 package.json。routePrefix决定了 API Reference 的访问路径默认值为/reference。源码中getRoutePrefix会去掉尾部的/统一处理带不带斜杠的写法见 fastifyApiReference.ts#L31-L36。文档来源content、url 与 fastify/swagger 三级优先级如果你已有 OpenAPI/Swagger 文档可以直接把 URL 传给插件// Render an API reference for a given OpenAPI/Swagger spec URL fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, configuration: { title: Our API Reference, url: /openapi.json, }, })如果项目中已经注册了fastify/swagger插件会自动从其生成的fastify.swagger()中取文档此时什么都不用配await fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, })这里的优先级在源码中有明确实现插件在注册时依次探测configuration.content→configuration.url→fastify/swagger见 fastifyApiReference.ts#L76-L105configuration.content直接传入 OpenAPI 文档对象值得注意的是content也支持传入一个函数返回文档对象源码会按需调用它取值。这允许你在请求时动态构造 spec例如按租户返回不同文档。测试用例 fastifyApiReference.test.ts 中对content: spec与content: () spec两种形式都做了覆盖。configuration.urlReference 页面会在浏览器端直接加载该 URL同源或跨域均可。fastify/swagger通过fastify.hasPlugin(fastify/swagger)判断是否注册且要求fastify.swagger是函数若fastify/swagger使用了decorator选项导致该函数不可用则会跳过。如果三者都未提供且没有配置sources插件不会报错而是打出一条警告后正常退出[scalar/fastify-api-reference] You didnt provide a content, url, sources or fastify/swagger could not be found. Please provide one of these options.这一行为同样有测试用例保障见 fastifyApiReference.test.ts#L496-L514。插件接收的configuration是 Scalar 的通用配置对象完整字段说明见 configuration。插件选项Options一览插件接受以下选项类型定义见 integrations/fastify/src/types.tsOptionTypeDefaultDescriptionroutePrefixstring/referenceAPI Reference 的访问路径前缀。configurationobject–Scalar 通用配置对象url、content、sources、title、theme、layout等完整见 configuration。openApiDocumentEndpointsobject{ json: /openapi.json, yaml: /openapi.yaml }OpenAPI 文档再暴露的端点路径相对于routePrefix。hooksobject–作用于插件注册的所有路由的 FastifyonRequest与preHandler钩子常用于鉴权。logLevelstring–插件路由的日志级别取值fatal \| error \| warn \| info \| debug \| trace \| silent设为silent可静默这些路由的日志。再暴露 OpenAPI 文档端点当你通过configuration.content或fastify/swagger提供文档时插件会在routePrefix下把解析后的文档重新暴露出来方便其他工具消费/reference/openapi.json/reference/openapi.yaml文档会先经过scalar/openapi-parser的normalize归一化再分别用toJson/toYaml输出响应同时带有Content-Disposition: filenameslug.json|.yaml文件名取自 spec 标题经 slug 化处理见 fastifyApiReference.ts#L130-L179并附带宽松的 CORS 头Access-Control-Allow-Origin: *与Access-Control-Allow-Methods: *外部工具可以直接抓取。CORS 行为由测试明确断言见 fastifyApiReference.test.ts#L269-L293。两个重要的实现细节configuration.url来源不会再暴露。因为此时浏览器直接加载你自己的 URL本地再暴露一份没有意义测试也验证了该场景下/reference/openapi.json与/reference/openapi.yaml返回 404见 fastifyApiReference.test.ts#L541-L565。只有 HTML 页面内的 spec 引用是相对路径./openapi.json以便在反向代理下正常工作而文档端点本身只在content/swagger来源下注册。端点路径可以用openApiDocumentEndpoints修改await fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, openApiDocumentEndpoints: { json: /openapi.json, yaml: /openapi.yaml, }, })Content Security PolicyCSP与 nonce插件把 Scalar 的 standalone bundle 从你自己的源上提供服务路径为${routePrefix}/js/scalar.js因此无需在 CSP 中放行 CDN ——script-src self即可覆盖。若要在不含script-src unsafe-inline也不含unsafe-eval的严格 CSP 下运行传入一个nonce即可。它会作用于插件输出的内联script与style标签同时通过meta propertycsp-nonce暴露给 bundle使其在运行时注入的样式表也携带同一 nonce渲染逻辑见 packages/client-side-rendering/src/html-rendering.ts#L132-L160await fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, configuration: { nonce: r4nd0m-nonce-value, }, })与之匹配的响应头策略形如Content-Security-Policy: script-src self nonce-r4nd0m-nonce-value; style-src self unsafe-inline注意style-src仍需要unsafe-inline。Reference 会渲染内联style...属性而 nonce 无法授权这类属性——这是 CSP 规范本身的限制不是插件的问题。注意nonce在插件注册时被读取一次之后每个响应都发送同一个值目前不支持按请求生成 nonce。从渲染源码看当设置了nonce时页面默认回退到单文件 UMD bundle因为 ESM 构建通过原生import拉取的分块脚本无法携带 nonce见 html-rendering.ts#L214-L267。此外根据使用场景可能还需要扩展其他指令例如connect-src让内置 API Client 能访问你的 API、img-src/font-src文档或主题引用的图片与字体。主题Themes默认使用为 Fastify 场景定制的内置主题。你也可以在configuration中指定其他主题全部主题见 themesawait fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, configuration: { theme: purple, }, })日志logLevel插件与 Fastify 的 logger 完全兼容。通过logLevel可以单独控制插件注册的路由的日志级别fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, logLevel: silent, })源码中logLevel会被展开到插件注册的每一条路由上HTML 页面、JS 文件、JSON/YAML 文档端点。测试用例通过自定义 logger serializer 断言当logLevel: silent时对上述所有路由发起请求后不会产生任何请求日志见 fastifyApiReference.test.ts#L457-L494。鉴权用 hooks 保护 API Reference由于插件注册的是标准 Fastify 路由你可以通过hooks选项套用任何 Fastify 鉴权插件。下面以fastify/basic-auth为例import FastifyBasicAuth from fastify/basic-auth await fastify.register(FastifyBasicAuth, { validate(username, password, request, reply, done) { if (username admin password admin) { done() } else { done(new Error(Access denied)) } }, authenticate: true, }) await fastify.register(import(scalar/fastify-api-reference), { routePrefix: /reference, hooks: { onRequest: fastify.basicAuth, }, })onRequest与preHandler会应用到插件注册的所有路由——HTML 页面、打包的 JS 文件、OpenAPI 文档端点一个都不漏。这一行为有直接的测试验证未带Authorization头时/reference与/reference/js/scalar.js均返回 401带上正确凭据后均返回 200见 fastifyApiReference.test.ts#L403-L455。完整实战从零搭一个带 API Reference 的 Fastify 项目第一步创建 Fastify 项目可选从零开始的话先安装 Fastifynpm init npm install fastify然后创建index.js// index.js import Fastify from fastify // Instantiate the framework const fastify Fastify({ logger: true, }) // Declare a route fastify.get(/, function (request, reply) { reply.send({ hello: world }) }) // Run the server fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } console.log(Fastify is now listening on ${address}) })由于这里使用了 ES Module 的import语法直接运行会失败需要在package.json中补充{ name: my-fastify-app, version: 1.0.0, description: , main: index.js, type: module, scripts: { dev: npx nodemon index.js, test: echo \Error: no test specified\ exit 1 }, … }其中type: module是让 Node 支持 ES Module 的关键dev脚本只是图方便用 nodemon 热重启。运行npm run dev输出中会打印服务地址http://localhost:3000浏览器打开即可看到示例 JSON。第二步接入 fastify/swagger可选别被 Swagger 这个名字误导——这是官方的 OpenAPI 3.0 生成包。安装npm install fastify/swagger然后替换index.js的内容import FastifySwagger from fastify/swagger import Fastify from fastify // Instantiate the framework const fastify Fastify({ logger: true, }) // Set up fastify/swagger await fastify.register(FastifySwagger, { openapi: { info: { title: My Fastify App, version: 1.0.0, }, components: { securitySchemes: { apiKey: { type: apiKey, name: apiKey, in: header, }, }, }, }, }) fastify.put( /example-route/:id, { schema: { description: post some data, tags: [user, code], summary: qwerty, security: [{ apiKey: [] }], params: { type: object, properties: { id: { type: string, description: user id, }, }, }, body: { type: object, properties: { hello: { type: string }, obj: { type: object, properties: { some: { type: string }, }, }, }, }, response: { 201: { description: Successful response, type: object, properties: { hello: { type: string }, }, }, default: { description: Default response, type: object, properties: { foo: { type: string }, }, }, }, }, }, (req, reply) { reply.code(201).send({ hello: Hello ${req.body.hello} }) }, ) // Serve an OpenAPI file fastify.get(/openapi.json, async (request, reply) { return fastify.swagger() }) // Wait for Fastify await fastify.ready() // Run the server fastify.listen({ port: 3000 }, function (err, address) { if (err) { fastify.log.error(err) process.exit(1) } console.log(Fastify is now listening on ${address}) })这段代码的 TL;DR1注册 Swagger 插件2定义 API 名称等全局信息3为带schema的路由生成 OpenAPI 定义4暴露一个返回生成文档的 JSON 端点。重启服务后http://localhost:3000/openapi.json应该能返回一份像样的 OpenAPI 文件。仓库内的 playground 就是一个更完整的可运行示例注册fastify/swagger、定义带schema的 PUT/POST/GET/DELETE 四个路由最后register(Scalar, { routePrefix: / })默认监听 5053 端口。第三步用 Scalar 渲染 API Referencenpm install scalar/fastify-api-reference在index.js中、await fastify.ready()之前加入// … // Render the API reference import ScalarApiReference from scalar/fastify-api-reference await fastify.register(ScalarApiReference, { routePrefix: /reference, // Additional hooks for the API reference routes. You can provide the onRequest and preHandler hooks hooks: { onRequest: function (request, reply, done) { done() }, preHandler: function (request, reply, done) { done() }, }, }) // …说明import语句放在文件中间在 ESM 中是合法的会被提升当然更规范的做法是移到文件顶部。重启服务后访问http://localhost:3000/reference就能看到交互式 API Reference。此后继续给 Fastify 添加带schema的路由Reference 会随fastify/swagger的生成结果自动保持同步。第四步自定义可选把configuration对象传给插件即可定制大量细节layout、theme、darkMode、customCss等完整字段见 configurationTypeScript 下所有选项都有自动补全import ScalarApiReference from scalar/fastify-api-reference await fastify.register(ScalarApiReference, { routePrefix: /reference, configuration: { layout: classic, // 更多配置项见 documentation/configuration.md }, })进阶手工维护的 OpenAPI 文件自动生成的 OpenAPI 很方便但也有人偏好手工维护 spec。此时只需把url指向你已有的 OpenAPI 文件即可import ScalarApiReference from scalar/fastify-api-reference await fastify.register(ScalarApiReference, { routePrefix: /reference, configuration: { // On your domain: url: /openapi.json, }, })注意如果不使用fastify/swagger来生成并托管 spec需要自己把它作为静态文件服务出去例如使用fastify/static。源码级实现要点了解以下实现细节有助于排查代理、CSP 与构建问题HTML 页面由renderApiReference生成。插件的 HTML 路由调用scalar/client-side-rendering的 renderApiReference 输出完整 HTML 文档div idapp/div加上加载 bundle 的script及序列化后的配置。配置序列化serializeConfigToJs会保留函数值以toString()输出保证回调类配置项能穿过内联script边界。bundle 自托管。getJavaScriptFile从scalar/api-reference读取 standalone 构建在发布的 npm 包中Vite 构建阶段会直接把脚本内联为字符串见 getJavaScriptFile.ts因此产物自包含、不再运行时读文件——把 Fastify 应用打包进 Docker 镜像时也不会因缺失构建产物而失效。尾斜杠重定向。为了让页面内的 JS 引用js/scalar.js保持相对路径插件会把不带尾斜杠的/reference301 重定向到/reference/若应用设置了ignoreTrailingSlash: true兼容 Fastify 顶层配置与 v6 的routerOptions位置则跳过该路由。带插件prefix前缀如register(inner, { prefix: /api })时的重定向也已被测试覆盖见 fastifyApiReference.test.ts#L97-L141。对 fastify/swagger 透明。插件为所有路由挂上hide: true的 schema即使fastify/swagger已注册这些路由也不会出现在生成的文档里见 fastifyApiReference.ts#L17-L29同时它不依赖fastify/html等任何装饰器测试中显式断言fastify.html保持未定义。小结scalar/fastify-api-reference把从 OpenAPI 到可交互 API Reference压缩成一次register调用文档来源支持content对象或函数、url与fastify/swagger自动探测三级优先级content/swagger来源下自动再暴露带 CORS 头的 JSON/YAML 端点hooks、logLevel、CSPnonce、configuration通用配置则分别解决鉴权、日志、安全策略与外观定制。配合仓库内的 playground 示例 与 测试用例可以快速验证每一个选项的实际行为。【免费下载链接】scalarScalar is an open-source API platform: Modern REST API Client Beautiful API References ✨ 1st-Class OpenAPI/Swagger Support项目地址: https://gitcode.com/GitHub_Trending/sc/scalar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表