服务器操作
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
服务器操作(Server Actions)允许你在服务器端定义函数,并直接从客户端组件调用,框架会抽象处理网络层。
通过 tRPC 过程(procedures)定义服务器操作,你可以获得 tRPC 的所有内置功能:输入验证、通过中间件(middleware)实现的认证授权、输出验证、数据转换器等。
服务器操作集成使用 experimental_ 前缀,目前仍在积极开发中。API 可能会在未来的版本中变更。
设置服务器操作过程
1. 使用 experimental_caller 定义基础过程
在过程构建器上使用 experimental_caller 和 experimental_nextAppDirCaller 来创建可直接作为普通函数调用的过程(即服务器操作)。pathExtractor 选项允许你通过元数据识别过程,这对于日志记录和可观察性非常有用,因为服务器操作没有类似 user.byId 的路由路径。
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),);
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),);
2. 通过中间件添加上下文
由于服务器操作不经过 HTTP 适配器,因此没有 createContext 来注入上下文。取而代之的是,使用中间件提供上下文(例如会话数据):
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';import {currentUser } from '../auth';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),).use (async (opts ) => {constuser = awaitcurrentUser ();returnopts .next ({ctx : {user } });});
server/trpc.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {experimental_nextAppDirCaller } from '@trpc/server/adapters/next-app-dir';import {currentUser } from '../auth';interfaceMeta {span : string;}export constt =initTRPC .meta <Meta >().create ();export constserverActionProcedure =t .procedure .experimental_caller (experimental_nextAppDirCaller ({pathExtractor : ({meta }) => (meta asMeta )?.span ?? '',}),).use (async (opts ) => {constuser = awaitcurrentUser ();returnopts .next ({ctx : {user } });});
3. 创建受保护的操作过程
3. 创建受保护的操作过程
server/trpc.tstsexport constprotectedAction =serverActionProcedure .use ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {...opts .ctx ,user :opts .ctx .user , // ensures type is non-nullable},});});
server/trpc.tstsexport constprotectedAction =serverActionProcedure .use ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED',});}returnopts .next ({ctx : {...opts .ctx ,user :opts .ctx .user , // ensures type is non-nullable},});});
添加授权中间件,为需要认证的操作创建可复用的基础:
创建一个包含 "use server" 指令的文件,并使用过程构建器定义您的服务器操作:
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// opts.ctx.user is typed as non-nullable// opts.input is typed as { title: string }// Create the post...});
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// opts.ctx.user is typed as non-nullable// opts.input is typed as { title: string }// Create the post...});
由于 experimental_caller,该过程现在是一个普通的异步函数,可以用作服务器操作。
从客户端组件调用
导入服务器操作并在客户端组件中使用。服务器操作既支持渐进增强的 action 属性,也支持通过 onSubmit 进行编程式调用:
app/post-form.tsxtsx'use client';import {createPost } from '../_actions';export functionPostForm () {return (<form onSubmit ={async (e ) => {e .preventDefault ();consttitle = newFormData (e .currentTarget ).get ('title') as string;awaitcreatePost ({title });}}><input type ="text"name ="title" /><button type ="submit">Create Post</button ></form >);}
app/post-form.tsxtsx'use client';import {createPost } from '../_actions';export functionPostForm () {return (<form onSubmit ={async (e ) => {e .preventDefault ();consttitle = newFormData (e .currentTarget ).get ('title') as string;awaitcreatePost ({title });}}><input type ="text"name ="title" /><button type ="submit">Create Post</button ></form >);}
使用元数据增强可观察性
使用 .meta() 方法为操作添加日志记录或追踪标签。元数据中的 span 属性会传递给 pathExtractor,因此可被可观察性工具使用:
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .meta ({span : 'create-post' }).input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// ...});
app/_actions.tsts'use server';import {z } from 'zod';import {protectedAction } from '../server/trpc';export constcreatePost =protectedAction .meta ({span : 'create-post' }).input (z .object ({title :z .string (),}),).mutation (async (opts ) => {// ...});
何时使用服务器操作 vs 变更(mutations)
服务器操作并非用于替代所有 tRPC 变更。请考虑以下权衡:
-
使用服务器操作:当需要渐进增强(表单在不支持 JavaScript 时仍能工作),或操作不需要更新客户端 React Query 缓存时
-
使用
useMutation:当需要更新客户端缓存、展示乐观更新(optimistic updates)或管理 UI 中的复杂加载/错误状态时
你可以逐步采用服务器操作,与现有 tRPC API 并存——无需重写整个 API。