Démarrage rapide
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 →
tRPC combine des concepts de REST et GraphQL. Si vous n'êtes familier avec aucun des deux, consultez les Concepts clés.
Installation
tRPC est réparti entre plusieurs packages, vous pouvez donc installer uniquement ce dont vous avez besoin. Assurez-vous d'installer les packages souhaités dans les sections appropriées de votre base de code. Pour ce guide de démarrage rapide, nous resterons simples et n'utiliserons que le client vanilla. Pour les guides spécifiques aux frameworks, consultez l'utilisation avec React et l'utilisation avec Next.js.
- tRPC nécessite TypeScript >= 4.7.0
- Nous vous recommandons vivement d'utiliser
"strict": truedans votretsconfig.jsoncar nous ne prenons pas officiellement en charge le mode non strict.
Commencez par installer les packages @trpc/server et @trpc/client :
- npm
- yarn
- pnpm
- bun
shnpm install @trpc/server @trpc/client
shnpm install @trpc/server @trpc/client
shyarn add @trpc/server @trpc/client
shyarn add @trpc/server @trpc/client
shpnpm add @trpc/server @trpc/client
shpnpm add @trpc/server @trpc/client
shbun add @trpc/server @trpc/client
shbun add @trpc/server @trpc/client
Définition d'un routeur backend
Parcourons les étapes de création d'une API typée avec tRPC. Pour commencer, cette API contiendra trois endpoints avec ces signatures TypeScript :
tstype User = { id: string; name: string; };userList: () => User[];userById: (id: string) => User;userCreate: (data: { name: string }) => User;
tstype User = { id: string; name: string; };userList: () => User[];userById: (id: string) => User;userCreate: (data: { name: string }) => User;
1. Créer une instance de routeur
D'abord, initialisons le backend tRPC. Il est recommandé de le faire dans un fichier séparé et d'exporter des fonctions d'aide réutilisables plutôt que l'objet tRPC complet.
server/trpc.tstsimport {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/trpc.tstsimport {initTRPC } from '@trpc/server';/*** Initialization of tRPC backend* Should be done only once per backend!*/constt =initTRPC .create ();/*** Export reusable router and procedure helpers* that can be used throughout the router*/export constrouter =t .router ;export constpublicProcedure =t .procedure ;
Ensuite, initialisons notre instance de routeur principale, souvent appelée appRouter, à laquelle nous ajouterons ultérieurement des procédures. Enfin, exportons le type du routeur que nous utiliserons côté client.
server/index.tstsimport {router } from './trpc';constappRouter =router ({// ...});// Export type router type signature,// NOT the router itself.export typeAppRouter = typeofappRouter ;
server/index.tstsimport {router } from './trpc';constappRouter =router ({// ...});// Export type router type signature,// NOT the router itself.export typeAppRouter = typeofappRouter ;
2. Ajouter une procédure de requête
Utilisez publicProcedure.query() pour ajouter une procédure de requête au routeur.
Ce qui suit crée une procédure de requête appelée userList qui renvoie une liste d'utilisateurs depuis notre base de données :
server/index.tstsimport {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({userList :publicProcedure .query (async () => {// Retrieve users from a datasource, this is an imaginary databaseconstusers = awaitdb .user .findMany ();returnusers ;}),});
server/index.tstsimport {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({userList :publicProcedure .query (async () => {// Retrieve users from a datasource, this is an imaginary databaseconstusers = awaitdb .user .findMany ();returnusers ;}),});
3. Utiliser un parseur d'entrée pour valider les inputs
Pour implémenter la procédure userById, nous devons accepter une entrée client. tRPC permet de définir des parseurs d'entrée pour valider et parser l'input. Vous pouvez définir votre propre parseur ou utiliser une bibliothèque de validation comme zod, yup ou superstruct.
Vous définissez votre parseur d'entrée dans publicProcedure.input(), accessible ensuite dans la fonction de résolution comme ci-dessous :
- Vanilla
- Zod
- Yup
- Valibot
The input parser should be a function that validates and casts the input of this procedure. It should return a strongly typed value when the input is valid or throw an error if the input is invalid.
server/index.tstsconstappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server/index.tstsconstappRouter =router ({// ...userById :publicProcedure // The input is unknown at this time. A client could have sent// us anything so we won't assume a certain data type..input ((val : unknown) => {// If the value is of type string, return it.// It will now be inferred as a string.if (typeofval === 'string') returnval ;// Uh oh, looks like that input wasn't a string.// We will throw an error instead of running the procedure.throw newError (`Invalid input: ${typeofval }`);}).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
The input parser can be any ZodType, e.g. z.string() or z.object().
server.tstsimport {z } from 'zod';import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server.tstsimport {z } from 'zod';import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({// ...userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
The input parser can be any YupSchema, e.g. yup.string() or yup.object().
server.tstsimport * asyup from 'yup';import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
server.tstsimport * asyup from 'yup';import {db } from './db';import {publicProcedure ,router } from './trpc';constappRouter =router ({// ...userById :publicProcedure .input (yup .string ().required ()).query (async (opts ) => {const {input } =opts ;// Retrieve the user with the given IDconstuser = awaitdb .user .findById (input );returnuser ;}),});
With Valibot, you simply need to wrap your schema with TypeSchema.
server.tsts// @filename: trpc.ts// @include: trpc// @filename: db.ts// @include: db// @filename: server.ts// ---cut---import { wrap } from '@decs/typeschema';import { string } from 'valibot';import { db } from './db';import { publicProcedure, router } from './trpc';const appRouter = router({// ...userById: publicProcedure.input(wrap(string())).query(async (opts) => {const { input } = opts;// ^?// Retrieve the user with the given IDconst user = await db.user.findById(input);// ^?return user;}),});
server.tsts// @filename: trpc.ts// @include: trpc// @filename: db.ts// @include: db// @filename: server.ts// ---cut---import { wrap } from '@decs/typeschema';import { string } from 'valibot';import { db } from './db';import { publicProcedure, router } from './trpc';const appRouter = router({// ...userById: publicProcedure.input(wrap(string())).query(async (opts) => {const { input } = opts;// ^?// Retrieve the user with the given IDconst user = await db.user.findById(input);// ^?return user;}),});
Dans le reste de cette documentation, nous utiliserons zod comme bibliothèque de validation.
4. Ajouter une procédure de mutation
Comme GraphQL, tRPC distingue les procédures de requête et de mutation.
Le fonctionnement d'une procédure côté serveur diffère peu entre requête et mutation. Le nom de la méthode change, et l'utilisation côté client diffère - mais tout le reste est identique !
Ajoutons une mutation userCreate en la définissant comme nouvelle propriété de notre objet routeur :
server.tstsconstappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create a new user in the databaseconstuser = awaitdb .user .create (input );returnuser ;}),});
server.tstsconstappRouter =router ({// ...userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;// Create a new user in the databaseconstuser = awaitdb .user .create (input );returnuser ;}),});
Exposition de l'API
Maintenant que notre routeur est défini, nous pouvons l'exposer. tRPC propose de nombreux adaptateurs pour utiliser le framework backend de votre choix. Pour rester simple, nous utiliserons l'adaptateur standalone.
server/index.tstsimport {createHTTPServer } from '@trpc/server/adapters/standalone';import {router } from './trpc';constappRouter =router ({// ...});constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
server/index.tstsimport {createHTTPServer } from '@trpc/server/adapters/standalone';import {router } from './trpc';constappRouter =router ({// ...});constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
See the full backend code
server/db.tststypeUser = {id : string;name : string };// Imaginary databaseconstusers :User [] = [];export constdb = {user : {findMany : async () =>users ,findById : async (id : string) =>users .find ((user ) =>user .id ===id ),create : async (data : {name : string }) => {constuser = {id :String (users .length + 1), ...data };users .push (user );returnuser ;},},};
server/db.tststypeUser = {id : string;name : string };// Imaginary databaseconstusers :User [] = [];export constdb = {user : {findMany : async () =>users ,findById : async (id : string) =>users .find ((user ) =>user .id ===id ),create : async (data : {name : string }) => {constuser = {id :String (users .length + 1), ...data };users .push (user );returnuser ;},},};
server/trpc.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/trpc.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constrouter =t .router ;export constpublicProcedure =t .procedure ;
server/index.tstsimport {createHTTPServer } from "@trpc/server/adapters/standalone";import {z } from "zod";import {db } from "./db";import {publicProcedure ,router } from "./trpc";constappRouter =router ({userList :publicProcedure .query (async () => {constusers = awaitdb .user .findMany ();returnusers ;}),userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser = awaitdb .user .findById (input );returnuser ;}),userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;constuser = awaitdb .user .create (input );returnuser ;}),});export typeAppRouter = typeofappRouter ;constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
server/index.tstsimport {createHTTPServer } from "@trpc/server/adapters/standalone";import {z } from "zod";import {db } from "./db";import {publicProcedure ,router } from "./trpc";constappRouter =router ({userList :publicProcedure .query (async () => {constusers = awaitdb .user .findMany ();returnusers ;}),userById :publicProcedure .input (z .string ()).query (async (opts ) => {const {input } =opts ;constuser = awaitdb .user .findById (input );returnuser ;}),userCreate :publicProcedure .input (z .object ({name :z .string () })).mutation (async (opts ) => {const {input } =opts ;constuser = awaitdb .user .create (input );returnuser ;}),});export typeAppRouter = typeofappRouter ;constserver =createHTTPServer ({router :appRouter ,});server .listen (3000);
Utiliser votre nouveau backend côté client
Passons maintenant au code côté client pour découvrir la puissance de la sécurité des types de bout en bout. Lorsque nous importons le type AppRouter pour l'utiliser côté client, nous obtenons une sécurité typage complète pour notre système sans divulguer aucun détail d'implémentation au client.
1. Configurer le client tRPC
client/index.tstsimport {createTRPCProxyClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './server';// 👆 **type-only** import// Pass AppRouter as generic here. 👇 This lets the `trpc` object know// what procedures are available on the server and their input/output types.consttrpc =createTRPCProxyClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000',}),],});
client/index.tstsimport {createTRPCProxyClient ,httpBatchLink } from '@trpc/client';import type {AppRouter } from './server';// 👆 **type-only** import// Pass AppRouter as generic here. 👇 This lets the `trpc` object know// what procedures are available on the server and their input/output types.consttrpc =createTRPCProxyClient <AppRouter >({links : [httpBatchLink ({url : 'http://localhost:3000',}),],});
Les liens dans tRPC sont similaires aux liens dans GraphQL : ils nous permettent de contrôler le flux de données avant leur envoi au serveur. Dans l'exemple ci-dessus, nous utilisons le httpBatchLink, qui regroupe automatiquement plusieurs appels en une seule requête HTTP. Pour une utilisation plus approfondie des liens, consultez la documentation sur les liens.
2. Interrogation et mutation
Vous avez maintenant accès à vos procédures API via l'objet trpc. Essayez !
client/index.tsts// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
client/index.tsts// Inferred typesconstuser = awaittrpc .userById .query ('1');constcreatedUser = awaittrpc .userCreate .mutate ({name : 'sachinraja' });
Autocomplétion complète
Ouvrez votre Intellisense pour explorer votre API côté frontend. Vous y trouverez toutes vos routes de procédures accompagnées des méthodes pour les appeler.
client/index.tsts// Full autocompletion on your routestrpc .u ;
client/index.tsts// Full autocompletion on your routestrpc .u ;
Essayez par vous-même !
Prochaines étapes
Nous vous encourageons vivement à explorer les exemples d'applications pour comprendre comment tRPC s'intègre dans votre framework préféré.
Par défaut, tRPC convertira les types complexes comme Date en leur équivalent JSON (dans le cas de Date, en string). Si vous souhaitez préserver l'intégrité de ces types, la méthode la plus simple consiste à utiliser superjson comme transformateur de données.
tRPC inclut des outils côté client plus sophistiqués conçus pour les projets React et Next.js.