Fastify 如何接入 Zod Type Provider 实现路由类型推导?
【免费下载链接】fastifyFast and low overhead web framework, for Node.js项目地址: https://gitcode.com/GitHub_Trending/fa/fastify
如果你在 TypeScript 项目里用 Fastify 写路由,并且希望request.query、request.body的类型直接来自内联的 Zod schema,而不是每条路由手写一组泛型接口,就需要接入 Fastify 官方的 Zod Type Provider。本文按 Type Providers 文档给出的步骤,在 Fastify 中安装并配置@fastify/type-provider-zod,让路由 schema 在运行时由 Zod 校验,在编译期自动推导出请求类型。
前提:先有一个可编译的 TypeScript Fastify 项目
Type Provider 依赖 TypeScript 编译来体现推导效果,参照 TypeScript 文档的起步步骤:
npm init -y npm i fastify npm i -D typescript @types/node在package.json的"scripts"中加入:
{ "scripts": { "build": "tsc -p tsconfig.json", "start": "node index.js" } }初始化 TypeScript 配置:
npx tsc --init文档要求把tsconfig.json中的target设为es2017或更高,否则会出现 Fastify 的弃用警告。
安装 Zod Type Provider
npm i zod @fastify/type-provider-zod官方 Type Provider 包遵循@fastify/type-provider-{provider-name}命名约定,Fastify 支持的推导包包括json-schema-to-ts、typebox和zod,本文使用其中的 Zod 包。
配置:替换编译器并绑定 Provider
按 Type Providers 文档的 Zod 示例,在index.ts中做三件事:用 Zod 提供的编译器替换 Fastify 的校验/序列化编译器,再通过withTypeProvider<ZodTypeProvider>()启用类型推导。
import fastify from 'fastify' import { ZodTypeProvider, serializerCompiler, validatorCompiler } from '@fastify/type-provider-zod' import { z } from 'zod/v4' const server = fastify() server.setValidatorCompiler(validatorCompiler) server.setSerializerCompiler(serializerCompiler) server.withTypeProvider<ZodTypeProvider>().get('/route', { schema: { querystring: z.object({ foo: z.number(), bar: z.string() }) } }, (request, reply) => { // type Query = { foo: number, bar: string } const { foo, bar } = request.query // type safe! })说明两点:
setValidatorCompiler(validatorCompiler)和setSerializerCompiler(serializerCompiler)让 Fastify 在运行时使用 Zod 编译后的校验与序列化逻辑;withTypeProvider<ZodTypeProvider>()则负责编译期推导,两者缺一不可。- schema 中的
querystring直接放 Zod schema(z.object(...)),不需要像纯 JSON Schema 那样再单独声明一套 TypeScript 接口。
路由拆到模块里:用带 Provider 泛型的 FastifyInstance
当路由注册函数放在单独的模块时,传入的实例参数也要带上 Type Provider 泛型,否则推导会丢失。文档给出的模式是在FastifyInstance的五个泛型位置中,第五位填入 Provider 类型。将示例中的 TypeBox 替换为 Zod 后等价写法为:
// index.ts import fastify from 'fastify' import { ZodTypeProvider } from '@fastify/type-provider-zod' import { registerRoutes } from './routes' const server = fastify().withTypeProvider<ZodTypeProvider>() registerRoutes(server) server.listen({ port: 3000 })// routes.ts import { z } from 'zod/v4' import { FastifyInstance, FastifyBaseLogger, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from 'fastify' import { ZodTypeProvider } from '@fastify/type-provider-zod' type FastifyZod = FastifyInstance< RawServerDefault, RawRequestDefaultExpression<RawServerDefault>, RawReplyDefaultExpression<RawServerDefault>, FastifyBaseLogger, ZodTypeProvider >; export function registerRoutes(fastify: FastifyZod): void { fastify.get('/route', { schema: { querystring: z.object({ foo: z.number(), bar: z.string() }) } }, (req) => { // works const { foo, bar } = req.query }); }这里的type FastifyZod就是把文档示例中的 Provider 泛型位置换成ZodTypeProvider,其余四个泛型保持默认取值。
验证接入是否生效
- 运行
npm run build,即tsc -p tsconfig.json。类型推导发生在编译期:如果 handler 里const { foo, bar } = request.query能正常解构且访问request.query上不存在的属性会报类型错误,说明推导生效。 - 运行
npm run start,文档示例中的成功输出是控制台打印Server listening at <address>(例如Server listening at http://127.0.0.1:8080)。 - 用
curl localhost:3000/route?foo=1&bar=x确认路由可达。
作用域限制:Provider 类型不会全局传播
文档明确指出:Provider 类型不向全局传播。用register引入插件会进入新的作用域,新的作用域里必须再次调用withTypeProvider才能让推导工作:
server.register(plugin1) // wrong:没有重新绑定 Provider server.register(plugin2) // correctplugin2的正确做法是在插件内部重新拿一次带 Provider 的实例:
function plugin2(fastify: FastifyInstance, _opts, done): void { const server = fastify.withTypeProvider<ZodTypeProvider>() server.get('/route', { schema: { querystring: z.object({ foo: z.number(), bar: z.string() }) } }, (req) => { // works const { foo, bar } = req.query }); done() }由于类型不跨作用域传播,多个作用域中目前无法避免在每条路由的注册处重复绑定 Provider;反过来,这也意味着同一个应用的不同作用域可以分别使用不同的 Provider(例如一个插件用typebox、另一个用json-schema-to-ts),互不干扰。
继续深入
- Type Providers 参考文档:
json-schema-to-ts、TypeBox、Zod 三种官方 Provider 的完整示例与 Scoped Type-Provider 说明。 - TypeScript 参考文档:不依赖 Type Provider 时通过
server.get<{ Querystring, Body, Reply }>手写泛型的替代方案。 - TypeProvider 类型定义:
FastifyTypeProvider接口与CallValidatorTypeProvider等推导逻辑的源码定义,自定义 Provider 时可对照阅读。
【免费下载链接】fastifyFast and low overhead web framework, for Node.js项目地址: https://gitcode.com/GitHub_Trending/fa/fastify
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考