在tRPC中自定义Zod错误:如何接管Zod错误处理权
自定义tRPC中Zod错误的处理逻辑
要绕过tRPC默认的Zod错误处理,完全由开发者接管,有以下几种可行方案:
方法1:全局配置errorFormatter捕获ZodError
通过tRPC.initTRPC的errorFormatter选项,可以全局拦截所有错误,识别Zod验证错误后自定义处理逻辑。
import { initTRPC, TRPCError } from '@trpc/server'; import { ZodError } from 'zod'; const t = initTRPC.create({ errorFormatter({ shape, error }) { // 判断是否为Zod验证错误 if (error.cause instanceof ZodError) { // 方案1:返回自定义错误结构 return { ...shape, data: { ...shape.data, customError: { message: '输入参数不符合规则', fieldErrors: error.cause.flatten().fieldErrors, }, }, }; // 方案2:抛出自定义TRPCError // throw new TRPCError({ // code: 'BAD_REQUEST', // message: '参数验证失败,请检查输入', // data: error.cause.flatten(), // }); } // 非Zod错误保持默认格式化 return shape; }, });
方法2:路由级别手动验证输入(跳过tRPC默认验证)
如果只需要对特定路由做自定义处理,可以直接在procedure内手动执行Zod验证,绕过tRPC的默认验证拦截。
import { z } from 'zod'; import { initTRPC, TRPCError } from '@trpc/server'; const t = initTRPC.create(); const UserInputSchema = z.object({ name: z.string().min(2), age: z.number().min(18), }); const appRouter = t.router({ createUser: t.procedure // 不使用tRPC自带的input()验证 .mutation(async ({ input }) => { try { const validatedInput = UserInputSchema.parse(input); // 执行业务逻辑 return { id: 'user_1', ...validatedInput }; } catch (err) { if (err instanceof ZodError) { // 自定义错误处理,比如抛自定义错误或直接返回错误信息 throw new TRPCError({ code: 'BAD_REQUEST', message: '用户名至少2位,年龄需满18岁', data: err.flatten().fieldErrors, }); } // 其他错误正常抛出 throw err; } }), });
方法3:用Middleware统一处理路由的Zod验证
如果多个路由需要相同的自定义错误逻辑,可以封装一个验证中间件,复用验证和错误处理逻辑。
import { z } from 'zod'; import { initTRPC, TRPCError, MiddlewareFunction } from '@trpc/server'; const t = initTRPC.create(); // 封装验证中间件 const validateInput = <T extends z.ZodTypeAny>(schema: T): MiddlewareFunction<{ input: unknown }, { input: z.infer<T> }> => { return async ({ input, next }) => { try { const validated = schema.parse(input); return await next({ input: validated }); } catch (err) { if (err instanceof ZodError) { throw new TRPCError({ code: 'BAD_REQUEST', message: '参数验证失败', data: { details: err.flatten().fieldErrors, }, }); } throw err; } }; }; // 使用中间件的路由 const UserInputSchema = z.object({ name: z.string().min(2), age: z.number().min(18), }); const appRouter = t.router({ createUser: t.procedure .use(validateInput(UserInputSchema)) .mutation(async ({ input }) => { // input已通过验证,直接使用 return { id: 'user_1', ...input }; }), });
内容的提问来源于stack exchange,提问作者Noah -
相关产品推荐
相关产品推荐

