You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在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 -

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 10:16:23