Définir des procédures
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 →
Une procédure est une fonction exposée au client, qui peut être de l'un des types suivants :
-
une
Query- utilisée pour récupérer des données, généralement sans les modifier -
une
Mutation- utilisée pour envoyer des données, souvent pour créer/mettre à jour/supprimer -
une
Subscription- vous n'en aurez peut-être pas besoin, et nous avons une documentation dédiée
Les procédures dans tRPC sont des primitives très flexibles pour créer des fonctions backend. Elles utilisent un modèle de construction immuable, ce qui signifie que vous pouvez créer des procédures de base réutilisables partageant des fonctionnalités entre plusieurs procédures.
Écrire des procédures
L'objet t créé lors de la configuration tRPC retourne une t.procedure initiale sur laquelle toutes les autres procédures sont construites :
tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .context <{signGuestBook : () =>Promise <void> }>().create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;constappRouter =router ({// Queries are the best place to fetch datahello :publicProcedure .query (() => {return {message : 'hello world',};}),// Mutations are the best place to do things like updating a databasegoodbye :publicProcedure .mutation (async (opts ) => {awaitopts .ctx .signGuestBook ();return {message : 'goodbye!',};}),});
tsimport {initTRPC } from '@trpc/server';import {z } from 'zod';constt =initTRPC .context <{signGuestBook : () =>Promise <void> }>().create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;constappRouter =router ({// Queries are the best place to fetch datahello :publicProcedure .query (() => {return {message : 'hello world',};}),// Mutations are the best place to do things like updating a databasegoodbye :publicProcedure .mutation (async (opts ) => {awaitopts .ctx .signGuestBook ();return {message : 'goodbye!',};}),});
Procédures de base réutilisables
En règle générale, nous vous recommandons de renommer et d'exporter t.procedure en tant que publicProcedure, ce qui vous permet ensuite de créer d'autres procédures nommées pour des cas d'usage spécifiques et de les exporter également. Ce modèle appelé "procédures de base" est un mécanisme clé pour la réutilisation de code et de comportements dans tRPC ; presque toutes les applications en auront besoin.
Dans le code ci-dessous, nous utilisons des procédures de base réutilisables pour implémenter des cas courants dans notre application - nous créons une procédure de base pour les utilisateurs connectés (authedProcedure) et une autre qui prend un organizationId et valide qu'un utilisateur fait partie de cette organisation.
Ceci est un exemple simplifié ; en pratique vous voudrez probablement combiner Headers, Context, Middleware et Metadata pour authentifier et autoriser vos utilisateurs.
tsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeOrganization = {id : string;name : string;};typeMembership = {role : 'ADMIN' | 'MEMBER';Organization :Organization ;};typeUser = {id : string;memberships :Membership [];};typeContext = {/*** User is nullable*/user :User | null;};constt =initTRPC .context <Context >().create ();export constpublicProcedure =t .procedure ;// procedure that asserts that the user is logged inexport constauthedProcedure =t .procedure .use (async functionisAuthed (opts ) {const {ctx } =opts ;// `ctx.user` is nullableif (!ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}returnopts .next ({ctx : {// ✅ user value is known to be non-null nowuser :ctx .user ,},});});// procedure that a user is a member of a specific organizationexport constorganizationProcedure =authedProcedure .input (z .object ({organizationId :z .string () })).use (functionisMemberOfOrganization (opts ) {constmembership =opts .ctx .user .memberships .find ((m ) =>m .Organization .id ===opts .input .organizationId ,);if (!membership ) {throw newTRPCError ({code : 'FORBIDDEN',});}returnopts .next ({ctx : {Organization :membership .Organization ,},});});export constappRouter =t .router ({whoami :authedProcedure .query (async (opts ) => {// user is non-nullable hereconst {ctx } =opts ;returnctx .user ;}),addMember :organizationProcedure .input (z .object ({z .string ().}),).mutation ((opts ) => {// ctx contains the non-nullable user & the organization being queriedconst {ctx } =opts ;// input includes the validated email of the user being invited & the validated organizationIdconst {input } =opts ;return '...';}),});
tsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeOrganization = {id : string;name : string;};typeMembership = {role : 'ADMIN' | 'MEMBER';Organization :Organization ;};typeUser = {id : string;memberships :Membership [];};typeContext = {/*** User is nullable*/user :User | null;};constt =initTRPC .context <Context >().create ();export constpublicProcedure =t .procedure ;// procedure that asserts that the user is logged inexport constauthedProcedure =t .procedure .use (async functionisAuthed (opts ) {const {ctx } =opts ;// `ctx.user` is nullableif (!ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}returnopts .next ({ctx : {// ✅ user value is known to be non-null nowuser :ctx .user ,},});});// procedure that a user is a member of a specific organizationexport constorganizationProcedure =authedProcedure .input (z .object ({organizationId :z .string () })).use (functionisMemberOfOrganization (opts ) {constmembership =opts .ctx .user .memberships .find ((m ) =>m .Organization .id ===opts .input .organizationId ,);if (!membership ) {throw newTRPCError ({code : 'FORBIDDEN',});}returnopts .next ({ctx : {Organization :membership .Organization ,},});});export constappRouter =t .router ({whoami :authedProcedure .query (async (opts ) => {// user is non-nullable hereconst {ctx } =opts ;returnctx .user ;}),addMember :organizationProcedure .input (z .object ({z .string ().}),).mutation ((opts ) => {// ctx contains the non-nullable user & the organization being queriedconst {ctx } =opts ;// input includes the validated email of the user being invited & the validated organizationIdconst {input } =opts ;return '...';}),});
Inférer le type d'options d'une "procédure de base"
En plus de pouvoir inférer les types d'entrée et de sortie d'une procédure, vous pouvez aussi inférer le type d'options d'un constructeur de procédure spécifique (ou procédure de base) en utilisant inferProcedureBuilderResolverOptions.
Cet utilitaire de typage est utile pour déclarer un type aux paramètres d'une fonction. Par exemple, pour séparer le handler d'exécution principal d'une procédure de sa définition dans le routeur, ou pour créer une fonction utilitaire fonctionnant avec plusieurs procédures.
tsasync function getMembersOfOrganization(opts: inferProcedureBuilderResolverOptions<typeof organizationProcedure>,) {// input and ctx are now correctly typed! const { ctx, input } = opts;return await prisma.user.findMany({where: {membership: { organizationId: ctx.Organization.id, },},});}export const appRouter = t.router({listMembers: organizationProcedure.query(async (opts) => { // use helper function! const members = await getMembersOfOrganization(opts);return members;}),});
tsasync function getMembersOfOrganization(opts: inferProcedureBuilderResolverOptions<typeof organizationProcedure>,) {// input and ctx are now correctly typed! const { ctx, input } = opts;return await prisma.user.findMany({where: {membership: { organizationId: ctx.Organization.id, },},});}export const appRouter = t.router({listMembers: organizationProcedure.query(async (opts) => { // use helper function! const members = await getMembersOfOrganization(opts);return members;}),});
Abonnements
Pour les informations sur les abonnements, consultez notre guide dédié.