news 2026/9/14 17:48:31

Scalar + Next.js Route Handlers:用 Zod 4 生成 OpenAPI 描述并挂载 API Reference 的完整实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Scalar + Next.js Route Handlers:用 Zod 4 生成 OpenAPI 描述并挂载 API Reference 的完整实践

Scalar + Next.js Route Handlers:用 Zod 4 生成 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 仓库中 route-handlers.md 这份官方 Recipe 展开,讲解如何在纯 Next.js App Router 项目里,用 Route Handlers + Zod 4 显式描述路径、方法与状态码,用z.toJSONSchema自动生成响应 Schema,再通过@scalar/nextjs-api-reference把渲染后的 API Reference 挂到/scalar路由上。读完你可以独立完成"Zod Schema → OpenAPI 3.1 文档 → Scalar 可视化参考"这条链路,并理解ApiReference适配器在源码层面的工作方式与验证要点。

方案定位:显式描述路线,不依赖路由自动发现

Scalar 的 Next.js 配方共三种,对应不同的路由层:

配方路由层OpenAPI 来源
本文:Route Handlers + Zod原生 Next.js Route Handler手写paths,Zod 生成 Schema
Hono 配方Hono +@hono/zod-openapiapi.getOpenAPI31Document()从注册路由生成
oRPC 配方oRPC Fetch AdapterOpenAPIGenerator从 router 元数据生成

Route Handlers 配方的特点是:没有自动路由发现/openapi.json端点里写什么路径、方法、状态码,Scalar 就展示什么;Zod 只负责"从 Schema 生成 JSON Schema"这一半工作。如果你已经用 Hono 或 oRPC 这类自带路由元数据的框架,也可以直接走它们的生成通道,但本文覆盖的是零额外路由依赖的裸 Route Handlers 场景。

另外注意边界:Scalar 仓库里还有一个实验性包@scalar/nextjs-openapi,它是一个独立的 Route 扫描器(从源码看,packages/nextjs-openapi/src/openapi.ts 通过 TypeScript Compiler API 扫描app/api目录并读取 JSDoc 注释来产出 OpenAPI 3.1 文档),官方标注为 pre-alpha,与本文的渲染器@scalar/nextjs-api-reference两个不同的包,不要混用。

第一步:安装依赖

在 App Router 应用中安装渲染器与 Zod 4(Recipe 中明确锁定了zod@4,因为z.toJSONSchema是 Zod 4 的转换 API):

npm install @scalar/nextjs-api-reference zod@4

第二步:定义一次可复用的 Zod 响应 Schema

Recipe 的关键实践是"Schema 只定义一次":既作为接口数据的运行时校验(parse),又作为 OpenAPI 文档的 Schema 来源。以 route-handlers.md 中的示例为准:

// app/lib/planets.ts import { z } from 'zod' export const planetsSchema = z.array(z.object({ id: z.string(), name: z.string(), moons: z.number().int().nonnegative(), })) export const planets = planetsSchema.parse([ { id: 'earth', name: 'Earth', moons: 1 }, { id: 'mars', name: 'Mars', moons: 2 }, ])

要点:

  • planetsSchema描述"行星目录数组"这一响应结构,字段约束(string/int/ 非负数)会原样体现在生成的 JSON Schema 中;
  • planets是同一 Schema 解析出的实际数据,接口直接返回它,保证"文档与返回值永远一致"——这是比文档和代码分开维护更省心的写法;
  • 后续/openapi.json端点会直接导入planetsSchema,实现单一事实来源。

第三步:编写数据端点(Route Handler)

数据端点就是最普通的 Next.js Route Handler,不需要引入任何 Scalar 代码:

// app/api/planets/route.ts import { planets } from '../../lib/planets' export const GET = (): Response => Response.json(planets)

注意导入路径:app/api/planets/route.ts位于app/api/planets/目录下,回app两层再进入lib,所以是../../lib/planets

第四步:在描述端点中生成 OpenAPI 3.1 文档

新建app/openapi.json/route.ts,手动声明路径、方法与状态码,并用z.toJSONSchema(planetsSchema)注入响应 Schema:

// app/openapi.json/route.ts import { z } from 'zod' import { planetsSchema } from '../lib/planets' export const GET = (): Response => Response.json({ openapi: '3.1.0', info: { title: 'Planets API', version: '1.0.0' }, servers: [{ url: '/' }], paths: { '/api/planets': { get: { operationId: 'listPlanets', summary: 'List planets', responses: { '200': { description: 'The planet catalog.', content: { 'application/json': { schema: z.toJSONSchema(planetsSchema) } }, }, }, }, }, }, })

逐段说明:

  • openapi: '3.1.0':Zod 4 的z.toJSONSchema产出的是 JSON Schema 风格(type: ['array', 'null']这类 3.1 语义),与 OpenAPI 3.1 的 Schema 规则兼容,因此 Recipe 固定使用该版本;
  • servers: [{ url: '/' }]:相对服务器 URL。这是本方案的一个实用特性——相对 URL 会跟随当前站点域名,从本地localhost:3000到预览部署环境都能自动对应,无需按环境硬编码绝对地址;
  • paths['/api/planets']:路径必须与第三步 Route Handler 实际暴露的路径一致(app/api/planets/route.ts对应/api/planets)。这里没有任何自动发现,路径写错文档就能正常渲染但请求会 404;
  • operationId/summary:决定 Scalar 侧边栏中的条目命名(示例中显示为List planets);
  • responses['200'].content['application/json'].schema:这是 Zod 发挥作用的位置,z.toJSONSchema(planetsSchema)把数组结构展开为{ type: 'array', items: { ...id/name/moons... } }的 JSON Schema。对于转换规则覆盖不到的边缘 Schema 类型与可选项,建议以 Zod 官方文档的 JSON Schema 章节为准(原 Recipe 中的外链即指向该文档)。

每新增一个端点,就在这个对象里多写一个paths条目——这是"显式描述"路线的核心成本,换来的是对文档结构的完全控制。

第五步:挂载 Scalar 到 /scalar

// app/scalar/route.ts import { ApiReference } from '@scalar/nextjs-api-reference' export const GET = ApiReference({ url: '/openapi.json' })

ApiReference返回的不是直接可挂的组件,而是一个符合 Route Handler 约定的函数。仓库中 integrations/nextjs/src/ApiReference.ts 的源码可以看清它的完整行为:

  1. 合并默认配置(内置_integration: 'nextjs'标识)与传入配置;
  2. 调用@scalar/client-side-renderingrenderApiReference,把配置渲染为一段完整 HTML 文档,并注入 custom-theme.ts 中定义的双主题(light/dark 的--scalar-*CSS 变量);
  3. 返回Content-Type: text/html的 200Response

对应的单测 integrations/nextjs/test/ApiReference.test.ts 验证了这三点:ApiReference({})返回函数、响应头为text/html、渲染时默认配置被正确合入(_integration: 'nextjs')。也就是说/scalar是一个完全自包含的 HTML 端点,文档数据由浏览器在页面内通过url: '/openapi.json'拉取——这就是为什么openapi.jsonscalar必须是两个独立路由。

url以外的其他配置项(主题、认证、布局等)类型定义为HtmlRenderingConfiguration,可参考仓库的 configuration.md 完整传入。

验证与预期结果

启动并验证(与 Recipe 给出的验收标准一致):

npm run dev
  1. 打开http://localhost:3000/scalar,侧边栏应出现List planets条目;
  2. 打开Test Request面板发送请求,预期返回200,响应体为 Earth 与 Mars 两条记录;
  3. 直接访问/openapi.json,应能看到/api/planets路径及由 Zod 生成的数组响应 Schema。

小结与选型建议

  • 本配方的分工很清晰:Zod 管"Schema 从哪来",你手写paths管"文档长什么样",@scalar/nextjs-api-reference管"怎么渲染"。三者解耦,任何一环都可替换;
  • 相对serversURL 让同一份文档在本地与预览部署间零配置迁移;
  • 若你的路由层本身就是 Hono 或 oRPC,改用对应配方(hono.md、orpc.md)可以把paths也自动化;
  • 若想尝试自动扫描 Route Handlers 的方案,可关注仓库中的实验包 packages/nextjs-openapi(pre-alpha,默认扫描目录为app/api,见 packages/nextjs-openapi/src/openapi.ts 的OpenAPIConfig定义),它独立于本文的渲染器包。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 17:48:28

DeepSeek 官方仓库惊现 DeepSeek Harness 桌面端!

1. 引言 最近,DeepSeek 官方仓库中悄然出现了一个新项目——DeepSeek Harness 桌面端!这一消息迅速在开发者社区引发热议。作为 AI 领域的明星玩家,DeepSeek 的一举一动都备受关注,这次推出的桌面端工具究竟有何亮点?本…

作者头像 李华
网站建设 2026/9/14 17:47:59

西门子PLC在立体仓库自动化中的关键技术与实践

1. 项目背景与立体仓库自动化需求在现代化仓储物流体系中,立体仓库作为核心设施,其自动化程度直接影响整体运营效率。传统仓储模式存在空间利用率低(通常不足40%)、人工拣选错误率高(约3%-5%)等痛点。采用西…

作者头像 李华
网站建设 2026/9/14 17:47:11

从硬编码到 i18n:前端多语言改造实战指南

去年我接手一个已经上线两年的管理系统,代码里到处是写死的中文文案。“删除成功”“确定要删除这条记录吗”“操作失败,请稍后重试”……产品提了个需求:一个月后要发布英文版。我第一反应不是“哦好的”,而是倒吸一口凉气——因…

作者头像 李华
网站建设 2026/9/14 17:47:01

龙芯2K0300 MPU驱动移植与优化实战

1. 项目背景与核心挑战龙芯K系列处理器作为国产自主CPU的代表,在嵌入式与工控领域正逐步扩大应用版图。走马观碑组(Walking Horse and Viewing Stele Group)MPU驱动移植项目,本质上是要将特定内存保护单元(MPU&#xf…

作者头像 李华