如何修改最新create-t3-app创建受保护tRPC Procedure并解决类型错误?
解决T3 App中Clerk身份验证与tRPC上下文兼容问题
问题核心
修改createTRPCContext参数类型为trpcNext.CreateNextContextOptions后,RSC环境下的server.ts调用时因传入{headers: Heads}类型不匹配报错;同时初始用opts.headers调用getAuth时存在类型不兼容问题。
解决方案:让上下文函数兼容两种调用场景
我们需要让createTRPCContext同时支持API路由(带req/res)和RSC(仅带headers)两种调用场景,具体修改如下:
1. 更新createTRPCContext定义
在./src/server/api/trpc.ts中,将参数类型改为联合类型,并内部适配两种场景的身份验证获取:
import type * as trpcNext from "@trpc/server/adapters/next"; import { getAuth } from "@clerk/nextjs/server"; import { TRPCError } from "@trpc/server"; // 定义兼容RSC和API路由的上下文参数类型 type CreateContextOptions = | { headers: Headers } | trpcNext.CreateNextContextOptions; export const createTRPCContext = async (opts: CreateContextOptions) => { // 根据参数类型选择正确的getAuth调用方式 const auth = "req" in opts ? getAuth(opts.req) : getAuth(opts.headers); const { userId } = auth; return { db, userId, // 统一传递headers,方便后续使用 headers: "headers" in opts ? opts.headers : opts.req.headers, }; }; // 定义privateProcedure export const privateProcedure = t.procedure.use(async ({ ctx, next }) => { if (!ctx.userId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "必须登录才能执行此操作", }); } // 向下传递确保userId非空的上下文 return next({ ctx: { ...ctx, userId: ctx.userId, }, }); });
2. 保留server.ts中的原有调用
此时./src/trpc/server.ts里的createContext函数无需修改,因为{headers: heads}完全符合我们定义的联合类型:
// ./src/trpc/server.ts import "server-only"; import { headers } from "next/headers"; import { cache } from "react"; import { createCaller } from "@/server/api/root"; import { createTRPCContext } from "@/server/api/trpc"; const createContext = cache(() => { const heads = new Headers(headers()); heads.set("x-trpc-source", "rsc"); return createTRPCContext({ headers: heads, }); }); export const api = createCaller(createContext);
3. 验证API路由兼容性
tRPC的Next.js适配器在API路由中调用createTRPCContext时,会传入trpcNext.CreateNextContextOptions类型的参数,我们的联合类型也能完美适配,无需额外修改。
关键说明
- 联合类型
CreateContextOptions覆盖了RSC和API路由两种调用场景,解决了类型不匹配问题 - 通过
"req" in opts类型守卫,在函数内部区分两种场景,正确调用getAuth privateProcedure中通过校验userId是否存在,实现了身份验证拦截
内容的提问来源于stack exchange,提问作者Austin Thaldorf-Huelsbeck
相关产品推荐
相关产品推荐

