如何在 Next.js App Router 用 tRPC 的 experimental_caller 定义 Server Actions 并注入上下文

发布时间:2026/9/10 18:35:05
如何在 Next.js App Router 用 tRPC 的 experimental_caller 定义 Server Actions 并注入上下文 如何在 Next.js App Router 用 tRPC 的 experimental_caller 定义 Server Actions 并注入上下文【免费下载链接】trpc‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc在 Next.js App Router 项目中你可能希望像普通 tRPC 一样保留输入校验、中间件鉴权和数据转换但又不想走 HTTP 路由——而是把 procedure 直接定义成 Server Action从客户端组件以函数调用的方式执行。tRPC 提供了experimental_caller方法配合experimental_nextAppDirCalleradapter 来完成这件事procedure 会变成一个普通异步函数可以直接作为 server action 使用由于 Server Action 不经过 HTTP adapter没有createContext可用上下文例如会话用户需要改用中间件注入。前提条件文档明确说明必须使用 Next.js App RouterPages Router 不支持使用的 API 仅在 tRPC v11 提供该集成使用experimental_前缀仍在活跃开发中文档明确提示 API 可能在后续版本变化。准备依赖与现有项目结构如果项目还没有接入 tRPC先按 App Router 安装文档 安装依赖该文档给出的完整集合为trpc/server、trpc/client、trpc/tanstack-react-query、tanstack/react-querylatest、zod、client-only、server-only。本文的 Server Action 主路径只依赖trpc/server与zodnpm i trpc/server zod如果项目已经按 App Router setup 文档 初始化了initTRPC注意本文示例使用initTRPC.metaMeta().create()——需要在初始化链上加上.metaMeta()因为后续会用 metadata 来标识 procedure见下文pathExtractor。定义基础 procedureexperimental_caller 加 experimental_nextAppDirCaller在server/trpc.ts中初始化 tRPC并用experimental_caller创建可被当作 server action 调用的基础 procedure// server/trpc.ts import { initTRPC, TRPCError } from trpc/server; import { experimental_nextAppDirCaller } from trpc/server/adapters/next-app-dir; interface Meta { span: string; } export const t initTRPC.metaMeta().create(); export const serverActionProcedure t.procedure.experimental_caller( experimental_nextAppDirCaller({ pathExtractor: ({ meta }) (meta as Meta)?.span ?? , }), );两点用途说明来自文档experimental_caller自定义 procedure 被当作函数调用时的行为配合experimental_nextAppDirCaller使其兼容 Next.js该 adapter 会处理客户端用useActionState包裹 action 时参数签名变化的情况此时表单数据变成第二个参数adapter 会自动取正确的位置。pathExtractor从 metadata 中提取一个路径字符串。Server Action 没有像user.byId这样的 router 路径pathExtractor提供的路径可供日志和可观测性工具区分不同 procedure。experimental_nextAppDirCaller的入口定义见 adapter 导出文件其配置项的完整说明pathExtractor、onError、normalizeFormData、createContext可参考 nextAppDirCaller 源码。用中间件注入上下文Server Action 不经过 HTTP adapter因此没有createContext回调。文档给出的做法是用一个中间件在调用链最前端注入上下文例如把当前会话用户放进ctx// server/auth.ts —— 文档示例为占位实现实际项目中替换为你的会话查询 export interface User { id: string; name: string; } export async function currentUser(): PromiseUser | null { return null; } // server/trpc.ts接上文 import { currentUser } from ./auth; export const serverActionProcedure t.procedure .experimental_caller( experimental_nextAppDirCaller({ pathExtractor: ({ meta }) (meta as Meta)?.span ?? , }), ) .use(async (opts) { const user await currentUser(); return opts.next({ ctx: { user } }); });替代路径从 nextAppDirCaller 源码 可以看到 adapter 配置本身也接受createContext回调adapter 测试 中有nextAppDirCaller({ createContext: () ({ foo: bar }) })的用法创建失败时会抛出code: INTERNAL_SERVER_ERROR、message: Failed to create context的TRPCError。文档主路径演示的是中间件方式两种方式可同时参考。增加鉴权保护protectedAction在基础 procedure 上再挂一个授权中间件得到要求登录才可调用的可复用 procedure。未登录时抛出UNAUTHORIZED并且通过显式透传让ctx.user的类型收窄为非可空// server/trpc.ts接上文 export const protectedAction serverActionProcedure.use((opts) { if (!opts.ctx.user) { throw new TRPCError({ code: UNAUTHORIZED, }); } return opts.next({ ctx: { ...opts.ctx, user: opts.ctx.user, // ensures type is non-nullable }, }); });如果你有现成的鉴权中间件可以直接复用不必内联定义。在 use server 文件中定义 action新建一个带use server指令的文件用 procedure 链定义 action。得益于experimental_caller这个 procedure 现在就是一个普通异步函数可以直接作为 server action 导出// app/_actions.ts use server; import { z } from zod; import { protectedAction } from ../server/trpc; export const createPost protectedAction .input( z.object({ title: z.string(), }), ) .mutation(async (opts) { // opts.ctx.user 的类型是非可空的 // opts.input 的类型是 { title: string } // 在这里创建 post…… });类型层面的检查点在mutation的回调里查看opts.ctx.user和opts.input如果前面步骤配置正确opts.ctx.user应显示为非可空类型、opts.input应显示为{ title: string }。从客户端组件调用在 client component 中导入 action 并调用。文档说明 Server Action 同时支持两种触发方式表单action属性渐进增强无 JavaScript 时表单仍可工作和onSubmit中的编程式调用// app/post-form.tsx use client; import { createPost } from ../_actions; export function PostForm() { return ( form action{createPost} onSubmit{async (e) { e.preventDefault(); const title new FormData(e.currentTarget).get(title) as string; await createPost({ title }); }} input typetext nametitle / button typesubmitCreate Post/button /form ); }提交后请求会走完整链路normalizeFormDataadapter 默认开启会把FormData转成Record再交给 procedure随后依次经过输入校验、鉴权中间件最后执行mutation回调。可选用 metadata 补充可观测性用.meta()给 action 打上标签span会被传入前面定义的pathExtractor日志或 tracing 工具就能区分不同 action// app/_actions.ts export const createPost protectedAction .meta({ span: create-post }) .input( z.object({ title: z.string(), }), ) .mutation(async (opts) { // ... });验证方式仓库中有一份针对该 adapter 的测试 next-app-dir.test.ts展示了可核对的预期行为以下为文档测试断言带experimental_caller(nextAppDirCaller({ normalizeFormData: true }))的 query procedure 可以直接以await proc()调用并得到返回值测试中断言结果为hellopathExtractor生效时依赖opts.path的日志中间件能拿到 metadata 中的 span测试中 logger 断言输出包含hello took。在自己的项目中最直接的验证是提交表单后确认 action 在服务端执行了mutation回调未登录时确认抛出UNAUTHORIZED类型层面确认opts.ctx.user非可空。限制与选型实验性 API文档明确标注 Server Actions 集成带experimental_前缀、仍在活跃开发API 可能变化。错误处理方面官方博客 提到当时仍有工作在进行。不支持 subscription从 nextAppDirCaller 源码 可见subscription类型的 procedure 会抛出NOT_IMPLEMENTEDNot implemented for type ...——该 caller 只实现query与mutation。配套导出同一入口文件还导出experimental_redirect与experimental_notFound可用于在 action 中做 Next.js 的跳转/404 处理。Server Actions 不是所有 mutation 的替代。文档给出的判断标准需要渐进增强无 JS 也能提交表单或不需要更新客户端 React Query 缓存时用 Server Actions需要更新客户端缓存、乐观更新或复杂 loading/error 状态时继续用useMutation。Server Actions 与现有 tRPC API 可以渐进式共存文档明确说明可以把现有 API 中的单个 procedure 逐个迁移成 server action没有必要重写整个 API。当你同时需要服务端执行与客户端缓存联动时同一套中间件和 procedure 构造方式可以两边复用。【免费下载链接】trpc‍♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询