Validateurs d'entrée et de sortie
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 procédures tRPC peuvent définir une logique de validation pour leurs entrées et/ou sorties. Ces validateurs servent également à inférer les types des données traitées. Nous offrons un support natif pour de nombreuses bibliothèques de validation populaires, et vous pouvez intégrer d'autres validateurs non directement supportés.
Validateurs d'entrée
En définissant un validateur d'entrée, tRPC peut vérifier qu'un appel de procédure est correct et renvoyer une erreur de validation le cas échéant.
Pour configurer un validateur d'entrée, utilisez la méthode procedure.input() :
ts// @target: esnextimport { initTRPC } from '@trpc/server';// ---cut---// Our examples use Zod by default, but usage with other libraries is identicalimport { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(z.object({name: z.string(),}),).query((opts) => {const name = opts.input.name;// ^?return {greeting: `Hello ${opts.input.name}`,};}),});
ts// @target: esnextimport { initTRPC } from '@trpc/server';// ---cut---// Our examples use Zod by default, but usage with other libraries is identicalimport { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(z.object({name: z.string(),}),).query((opts) => {const name = opts.input.name;// ^?return {greeting: `Hello ${opts.input.name}`,};}),});
Fusion des entrées
.input() peut être chaîné pour construire des types plus complexes, ce qui est particulièrement utile lorsque vous souhaitez réutiliser une entrée commune pour plusieurs procédures via un middleware.
ts// @target: esnextimport { initTRPC, TRPCError } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();// ---cut---const baseProcedure = t.procedure.input(z.object({ townName: z.string() })).use((opts) => {const input = opts.input;// ^?console.log(`Handling request with user from: ${input.townName}`);return opts.next();});export const appRouter = t.router({hello: baseProcedure.input(z.object({name: z.string(),}),).query((opts) => {const input = opts.input;// ^?return {greeting: `Hello ${input.name}, my friend from ${input.townName}`,};}),});
ts// @target: esnextimport { initTRPC, TRPCError } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();// ---cut---const baseProcedure = t.procedure.input(z.object({ townName: z.string() })).use((opts) => {const input = opts.input;// ^?console.log(`Handling request with user from: ${input.townName}`);return opts.next();});export const appRouter = t.router({hello: baseProcedure.input(z.object({name: z.string(),}),).query((opts) => {const input = opts.input;// ^?return {greeting: `Hello ${input.name}, my friend from ${input.townName}`,};}),});
Validateurs de sortie
La validation des sorties n'est pas toujours aussi cruciale que celle des entrées, car tRPC offre une sécurité de type automatique en inférant le type de retour de vos procédures. Voici quelques raisons de définir un validateur de sortie :
-
Vérifier que les données renvoyées par des sources non fiables sont correctes
-
Garantir que vous ne renvoyez pas plus de données que nécessaire au client
Si la validation de sortie échoue, le serveur répondra avec une erreur INTERNAL_SERVER_ERROR.
ts// @target: esnextimport { initTRPC } from '@trpc/server';// @noErrors// ---cut---import { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.output(z.object({greeting: z.string(),}),).query((opts) => {return {gre,// ^|};}),});
ts// @target: esnextimport { initTRPC } from '@trpc/server';// @noErrors// ---cut---import { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.output(z.object({greeting: z.string(),}),).query((opts) => {return {gre,// ^|};}),});
Le validateur le plus basique : une fonction
Vous pouvez définir un validateur sans dépendances tierces, à l'aide d'une simple fonction.
Nous ne recommandons pas de créer un validateur personnalisé sans besoin spécifique, mais il est important de comprendre qu'il n'y a aucune magie ici - c'est simplement du TypeScript !
Dans la plupart des cas, nous vous conseillons d'utiliser une bibliothèque de validation.
tsimport { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input((value): string => {if (typeof value === 'string') {return value;}throw new Error('Input is not a string');}).output((value): string => {if (typeof value === 'string') {return value;}throw new Error('Output is not a string');}).query((opts) => {const { input } = opts;// ^?return `hello ${input}`;}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input((value): string => {if (typeof value === 'string') {return value;}throw new Error('Input is not a string');}).output((value): string => {if (typeof value === 'string') {return value;}throw new Error('Output is not a string');}).query((opts) => {const { input } = opts;// ^?return `hello ${input}`;}),});export type AppRouter = typeof appRouter;
Intégrations de bibliothèques
tRPC fonctionne immédiatement avec plusieurs bibliothèques de validation et d'analyse populaires. Voici quelques exemples d'utilisation avec des validateurs officiellement supportés.
Avec Zod
Zod est notre recommandation par défaut. Son écosystème robuste en fait un excellent choix pour une utilisation dans différentes parties de votre codebase. Si vous n'avez pas de préférence et cherchez une bibliothèque puissante sans limitations futures, Zod est idéal.
tsimport { initTRPC } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(z.object({name: z.string(),}),).output(z.object({greeting: z.string(),}),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import { z } from 'zod';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(z.object({name: z.string(),}),).output(z.object({greeting: z.string(),}),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec Yup
tsimport { initTRPC } from '@trpc/server';import * as yup from 'yup';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(yup.object({name: yup.string().required(),}),).output(yup.object({greeting: yup.string().required(),}),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import * as yup from 'yup';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(yup.object({name: yup.string().required(),}),).output(yup.object({greeting: yup.string().required(),}),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec Superstruct
tsimport { initTRPC } from '@trpc/server';import { object, string } from 'superstruct';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(object({ name: string() })).output(object({ greeting: string() })).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import { object, string } from 'superstruct';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(object({ name: string() })).output(object({ greeting: string() })).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec scale-ts
tsimport { initTRPC } from '@trpc/server';import * as $ from 'scale-codec';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input($.object($.field('name', $.str))).output($.object($.field('greeting', $.str))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import * as $ from 'scale-codec';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input($.object($.field('name', $.str))).output($.object($.field('greeting', $.str))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec Typia
tsimport { initTRPC } from '@trpc/server';import typia from 'typia';import { v4 } from 'uuid';import { IBbsArticle } from '../structures/IBbsArticle';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({store: publicProcedure.input(typia.createAssert<IBbsArticle.IStore>()).output(typia.createAssert<IBbsArticle>()).query(({ input }) => {return {id: v4(),writer: input.writer,title: input.title,body: input.body,created_at: new Date().toString(),};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import typia from 'typia';import { v4 } from 'uuid';import { IBbsArticle } from '../structures/IBbsArticle';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({store: publicProcedure.input(typia.createAssert<IBbsArticle.IStore>()).output(typia.createAssert<IBbsArticle>()).query(({ input }) => {return {id: v4(),writer: input.writer,title: input.title,body: input.body,created_at: new Date().toString(),};}),});export type AppRouter = typeof appRouter;
Avec ArkType
tsimport { initTRPC } from '@trpc/server';import { type } from 'arktype';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(type({ name: 'string' }).assert).output(type({ greeting: 'string' }).assert).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import { type } from 'arktype';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(type({ name: 'string' }).assert).output(type({ greeting: 'string' }).assert).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec @effect/schema
tsimport * as Schema from '@effect/schema/Schema';import { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(Schema.decodeUnknownSync(Schema.Struct({ name: Schema.String }))).output(Schema.decodeUnknownSync(Schema.Struct({ greeting: Schema.String })),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport * as Schema from '@effect/schema/Schema';import { initTRPC } from '@trpc/server';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(Schema.decodeUnknownSync(Schema.Struct({ name: Schema.String }))).output(Schema.decodeUnknownSync(Schema.Struct({ greeting: Schema.String })),).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec runtypes
tsimport { initTRPC } from '@trpc/server';import * as T from 'runtypes';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(T.Record({ name: T.String })).output(T.Record({ greeting: T.String })).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { initTRPC } from '@trpc/server';import * as T from 'runtypes';const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(T.Record({ name: T.String })).output(T.Record({ greeting: T.String })).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Avec Valibot
tsimport { wrap } from '@decs/typeschema';import { initTRPC } from '@trpc/server';import { object, string } from 'valibot';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(wrap(object({ name: string() }))).output(wrap(object({ greeting: string() }))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
tsimport { wrap } from '@decs/typeschema';import { initTRPC } from '@trpc/server';import { object, string } from 'valibot';export const t = initTRPC.create();const publicProcedure = t.procedure;export const appRouter = t.router({hello: publicProcedure.input(wrap(object({ name: string() }))).output(wrap(object({ greeting: string() }))).query(({ input }) => {// ^?return {greeting: `hello ${input.name}`,};}),});export type AppRouter = typeof appRouter;
Contribuer avec votre propre bibliothèque de validation
Si vous développez une bibliothèque de validation prenant en charge tRPC, n'hésitez pas à ouvrir une PR pour cette page avec un exemple d'utilisation équivalent aux autres exemples présentés, ainsi qu'un lien vers votre documentation.
L'intégration avec tRPC se résume généralement à implémenter l'une des interfaces existantes. Dans certains cas, nous pouvons accepter une PR pour ajouter une nouvelle interface supportée. N'hésitez pas à ouvrir une issue pour discussion. Consultez les interfaces actuellement supportées :