Actions Serveur
Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →
Les Actions Serveur vous permettent de définir des fonctions sur le serveur et de les appeler directement depuis des composants clients, la couche réseau étant abstraite par le framework.
En définissant vos actions serveur via des procédures tRPC, vous bénéficiez de toutes ses fonctionnalités intégrées : validation des entrées, authentification et autorisation via des middlewares, validation des sorties, transformateurs de données, et plus encore.
L'intégration des Actions Serveur utilise le préfixe experimental_ et reste en développement actif. L'API peut évoluer dans les futures versions.
Configuration des procédures d'Action Serveur
1. Définir une procédure de base avec experimental_caller
Utilisez experimental_caller sur un constructeur de procédure avec experimental_nextAppDirCaller pour créer des procédures invocables comme fonctions standards (actions serveur). L'option pathExtractor permet d'identifier les procédures par métadonnées, utile pour la journalisation et l'observabilité car les actions serveur n'ont pas de chemin de routeur comme 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. Ajouter le contexte via middleware
Les actions serveur ne transitant pas par un adaptateur HTTP, il n'y a pas de createContext pour injecter le contexte. Utilisez plutôt un middleware pour fournir des éléments comme les données de session :
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. Créer une procédure d'action protégée
Ajoutez un middleware d'autorisation pour créer une base réutilisable pour les actions nécessitant une authentification :
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},});});
Définition d'une action serveur
Créez un fichier avec la directive "use server" et définissez votre action via le constructeur de procédure :
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...});
Grâce à experimental_caller, la procédure devient une fonction asynchrone standard utilisable comme action serveur.
Appel depuis des composants clients
Importez l'action serveur et utilisez-la dans un composant client. Les actions serveur fonctionnent avec l'attribut action pour l'amélioration progressive et les appels programmatiques via 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 >);}
Ajout d'observabilité avec des métadonnées
Utilisez la méthode .meta() pour étiqueter les actions en vue de journalisation ou de traçage. La propriété span des métadonnées est transmise à pathExtractor, permettant son utilisation par des outils d'observabilité :
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 ) => {// ...});
Quand utiliser les Actions Serveur plutôt que des mutations
Les Actions Serveur ne remplacent pas toutes les mutations tRPC. Pesez les compromis :
-
Privilégiez les Actions Serveur pour l'amélioration progressive (formulaires fonctionnant sans JavaScript), ou quand l'action ne nécessite pas de mettre à jour le cache React Query côté client.
-
Utilisez
useMutationlorsque vous devez mettre à jour le cache côté client, afficher des mises à jour optimistes, ou gérer des états de chargement/erreur complexes dans l'UI.
Vous pouvez adopter progressivement les actions serveur parallèlement à votre API tRPC existante - une réécriture complète n'est pas nécessaire.