Acciones de Servidor
Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →
Las Acciones de Servidor te permiten definir funciones en el servidor y llamarlas directamente desde componentes cliente, con la capa de red abstraída por el framework.
Al definir tus acciones de servidor mediante procedimientos de tRPC, obtienes todas las funcionalidades incorporadas de tRPC: validación de entradas, autenticación y autorización mediante middlewares, validación de salidas, transformadores de datos y más.
La integración de Acciones de Servidor utiliza el prefijo experimental_ y sigue en desarrollo activo. La API podría cambiar en futuras versiones.
Configuración de procedimientos para Acciones de Servidor
1. Define un procedimiento base con experimental_caller
Usa experimental_caller en un constructor de procedimientos junto con experimental_nextAppDirCaller para crear procedimientos que puedan invocarse como funciones simples (acciones de servidor). La opción pathExtractor te permite identificar procedimientos mediante metadatos, lo cual es útil para registro y observabilidad ya que las acciones de servidor no tienen rutas de router como 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. Añade contexto mediante middleware
Dado que las acciones de servidor no pasan por un adaptador HTTP, no hay createContext para inyectar contexto. En su lugar, usa un middleware para proporcionar contexto como datos de sesión:
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. Crea un procedimiento de acción protegida
Añade un middleware de autorización para crear una base reutilizable para acciones que requieran autenticación:
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},});});
Definición de una acción de servidor
Crea un archivo con la directiva "use server" y define tu acción usando el constructor de procedimientos:
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...});
Gracias a experimental_caller, el procedimiento ahora es una función asíncrona simple que puede usarse como acción de servidor.
Llamada desde componentes cliente
Importa la acción de servidor y úsala en un componente cliente. Las acciones de servidor funcionan tanto con el atributo action para mejora progresiva como con llamadas programáticas mediante 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 >);}
Añadiendo observabilidad con metadatos
Usa el método .meta() para etiquetar acciones con fines de registro o trazabilidad. La propiedad span de los metadatos se pasa a pathExtractor, por lo que puede ser utilizada por herramientas de observabilidad:
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 ) => {// ...});
Cuándo usar Acciones de Servidor vs mutaciones
Las Acciones de Servidor no reemplazan todas las mutaciones de tRPC. Considera las compensaciones:
-
Usa Acciones de Servidor cuando quieras mejora progresiva (formularios que funcionan sin JavaScript) o cuando la acción no necesite actualizar la caché de React Query en el cliente.
-
Usa
useMutationcuando necesites actualizar la caché del cliente, mostrar actualizaciones optimistas o gestionar estados complejos de carga/error en la UI.
Puedes adoptar acciones de servidor gradualmente junto a tu API existente de tRPC - no es necesario reescribir toda tu API.