Hoppa till huvudinnehållet
Version: 11.x

Validerare för indata och utdata

Inofficiell Beta-översättning

Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet. Hittade du ett fel? Rapportera problem →

tRPC-procedurer kan definiera valideringslogik för sina indata och/eller utdata. Validerare används också för att härleda typerna för indata och utdata (med hjälp av Standard Schema-gränssnittet om tillgängligt, eller anpassade gränssnitt för validerare som stöds). Vi har förstahandsstöd för många populära validerare, och du kan integrera validerare som vi inte direkt stöder.

Validerare för indata

Genom att definiera en indatavaliderare kan tRPC kontrollera att ett procedureanrop är korrekt och returnera ett valideringsfel om det inte är det.

För att konfigurera en indatavaliderare, använd metoden procedure.input():

ts
// Our examples use Zod by default, but usage with other libraries is identical
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(),
}),
)
.query((opts) => {
const name = opts.input.name;
const name: string
return {
greeting: `Hello ${opts.input.name}`,
};
}),
});
ts
// Our examples use Zod by default, but usage with other libraries is identical
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(),
}),
)
.query((opts) => {
const name = opts.input.name;
const name: string
return {
greeting: `Hello ${opts.input.name}`,
};
}),
});

Sammanfogning av indata

.input() kan staplas för att bygga mer komplexa typer, vilket är särskilt användbart när du vill utnyttja gemensam indata för en samling procedurer i en middleware.

ts
const baseProcedure = t.procedure
.input(z.object({ townName: z.string() }))
.use((opts) => {
const input = opts.input;
const input: { townName: string; }
 
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;
const input: { townName: string; name: string; }
return {
greeting: `Hello ${input.name}, my friend from ${input.townName}`,
};
}),
});
ts
const baseProcedure = t.procedure
.input(z.object({ townName: z.string() }))
.use((opts) => {
const input = opts.input;
const input: { townName: string; }
 
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;
const input: { townName: string; name: string; }
return {
greeting: `Hello ${input.name}, my friend from ${input.townName}`,
};
}),
});

Validerare för utdata

Validering av utdata är inte alltid lika viktigt som att definiera indata, eftersom tRPC ger dig automatisk typsäkerhet genom att härleda returtypen för dina procedurer. Några skäl att definiera en utdatavaliderare inkluderar:

  • Kontrollera att data som returneras från ej betrodda källor är korrekt

  • Säkerställa att du inte returnerar mer data till klienten än nödvändigt

info

Om utdatavalidering misslyckas kommer servern att svara med ett INTERNAL_SERVER_ERROR.

ts
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
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,
           
};
}),
});

Validering av utdata för prenumerationer

Eftersom prenumerationer är asynkra iteratorer kan du använda samma valideringstekniker som ovan.

Läs mer i prenumerationsguiden.

Den mest grundläggande valideraren: en funktion

Du kan definiera en validerare utan några tredjepartsberoenden, med en funktion.

info

Vi rekommenderar inte att skapa en anpassad validerare om du inte har ett specifikt behov, men det är viktigt att förstå att det inte finns någon magi här - det är bara TypeScript!

I de flesta fall rekommenderar vi att du använder ett valideringsbibliotek

ts
import { 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;
const input: string
return `hello ${input}`;
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { 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;
const input: string
return `hello ${input}`;
}),
});
 
export type AppRouter = typeof appRouter;

Biblioteksintegrationer

tRPC fungerar färdigt med ett antal populära validerings- och parsningsbibliotek, inklusive alla bibliotek som följer Standard Schema. Nedan följer några exempel på användning med validerare som vi officiellt stöder.

Med Zod

Zod är vårt standardrekommendation. Det har ett starkt ekosystem som gör det till ett utmärkt val att använda i flera delar av din kodbas. Om du inte har någon egen åsikt och vill ha ett kraftfullt bibliotek som inte begränsar framtida behov är Zod ett utmärkt val.

ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;

Med Yup

ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;

Med Superstruct

ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { 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 }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;

Med scale-ts

ts
import { 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 }) => {
(parameter) input: { readonly name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { 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 }) => {
(parameter) input: { readonly name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;

Med Typia

ts
import { 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;
ts
import { 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;

Med ArkType

ts
import { 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' })).query((opts) => {
return {
greeting: `hello ${opts.input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;
ts
import { 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' })).query((opts) => {
return {
greeting: `hello ${opts.input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;

Med effect

ts
import { initTRPC } from '@trpc/server';
import { Schema } from 'effect';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(Schema.standardSchemaV1(Schema.Struct({ name: Schema.String })))
.output(Schema.standardSchemaV1(Schema.Struct({ greeting: Schema.String })))
.query(({ input }) => {
// ^?
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;
ts
import { initTRPC } from '@trpc/server';
import { Schema } from 'effect';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(Schema.standardSchemaV1(Schema.Struct({ name: Schema.String })))
.output(Schema.standardSchemaV1(Schema.Struct({ greeting: Schema.String })))
.query(({ input }) => {
// ^?
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;

Med Valibot

ts
import { initTRPC } from '@trpc/server';
import * as v from 'valibot';
 
export const t = initTRPC.create();
 
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure
.input(v.object({ name: v.string() }))
.output(v.object({ greeting: v.string() }))
.query(({ input }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;
ts
import { initTRPC } from '@trpc/server';
import * as v from 'valibot';
 
export const t = initTRPC.create();
 
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure
.input(v.object({ name: v.string() }))
.output(v.object({ greeting: v.string() }))
.query(({ input }) => {
(parameter) input: { name: string; }
return {
greeting: `hello ${input.name}`,
};
}),
});
 
export type AppRouter = typeof appRouter;

Med @robolex/sure

Du kan definiera egna feltyper och en funktion för att kasta fel om det behövs. Som en bekvämlighet tillhandahåller @robolex/sure sure/src/err.ts:

ts
// sure/src/err.ts
export const err = (schema) => (input) => {
const [good, result] = schema(input);
if (good) return result;
throw result;
};
ts
// sure/src/err.ts
export const err = (schema) => (input) => {
const [good, result] = schema(input);
if (good) return result;
throw result;
};
ts
import { err, object, string } from '@robolex/sure';
import { initTRPC } from '@trpc/server';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(
err(
object({
name: string,
}),
),
)
.output(
err(
object({
greeting: string,
}),
),
)
.query(({ input }) => {
// ^?
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;
ts
import { err, object, string } from '@robolex/sure';
import { initTRPC } from '@trpc/server';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(
err(
object({
name: string,
}),
),
)
.output(
err(
object({
greeting: string,
}),
),
)
.query(({ input }) => {
// ^?
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;

Med TypeBox

ts
import { Type } from '@sinclair/typebox';
import { initTRPC } from '@trpc/server';
import { wrap } from '@typeschema/typebox';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(wrap(Type.Object({ name: Type.String() })))
.output(wrap(Type.Object({ greeting: Type.String() })))
.query(({ input }) => {
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;
ts
import { Type } from '@sinclair/typebox';
import { initTRPC } from '@trpc/server';
import { wrap } from '@typeschema/typebox';
export const t = initTRPC.create();
const publicProcedure = t.procedure;
export const appRouter = t.router({
hello: publicProcedure
.input(wrap(Type.Object({ name: Type.String() })))
.output(wrap(Type.Object({ greeting: Type.String() })))
.query(({ input }) => {
return {
greeting: `hello ${input.name}`,
};
}),
});
export type AppRouter = typeof appRouter;

Bidra med din egen valideringsbibliotek

Om du arbetar med ett valideringsbibliotek som stöder tRPC-användning, är du välkommen att öppna en PR för denna sida med motsvarande användning som i de andra exemplen här, samt en länk till din dokumentation.

Integration med tRPC är i de flesta fall lika enkelt som att uppfylla något av flera befintliga typergränssnitt. Att följa Standard Schema rekommenderas, men i vissa fall kan vi acceptera en PR för att lägga till ett nytt stött gränssnitt. Öppna gärna ett ärende för diskussion. Du kan se de befintliga stödda gränssnitten och funktionerna för parsning/validering i koden.