输入和输出验证器
tRPC 过程可以为其输入和/或输出定义验证逻辑,验证器也用于推断输入和输出的类型。我们对许多流行的验证器提供了原生支持,您可以 集成 我们不支持的验证器。
输入验证器
通过定义输入验证器,tRPC 可以检查过程调用是否正确,如果错误则返回验证错误。
要设置输入验证器,请使用 procedure.input()
方法
ts
// Our examples use Zod by default, but usage with other libraries is identicalimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constname =opts .input .name ;return {greeting : `Hello ${opts .input .name }`,};}),});
ts
// Our examples use Zod by default, but usage with other libraries is identicalimport {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constname =opts .input .name ;return {greeting : `Hello ${opts .input .name }`,};}),});
输入合并
.input()
可以堆叠起来构建更复杂的类型,这在您想将一些通用输入用于 中间件 中的一组过程中时特别有用。
ts
constbaseProcedure =t .procedure .input (z .object ({townName :z .string () })).use ((opts ) => {constinput =opts .input ;console .log (`Handling request with user from: ${input .townName }`);returnopts .next ();});export constappRouter =t .router ({hello :baseProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constinput =opts .input ;return {greeting : `Hello ${input .name }, my friend from ${input .townName }`,};}),});
ts
constbaseProcedure =t .procedure .input (z .object ({townName :z .string () })).use ((opts ) => {constinput =opts .input ;console .log (`Handling request with user from: ${input .townName }`);returnopts .next ();});export constappRouter =t .router ({hello :baseProcedure .input (z .object ({name :z .string (),}),).query ((opts ) => {constinput =opts .input ;return {greeting : `Hello ${input .name }, my friend from ${input .townName }`,};}),});
输出验证器
验证输出并不总是像定义输入那样重要,因为 tRPC 通过推断过程的返回类型为您提供了自动类型安全性。定义输出验证器的一些原因包括
- 检查从不受信任的来源返回的数据是否正确
- 确保您不会向客户端返回比必要更多的数据
如果输出验证失败,服务器将返回 INTERNAL_SERVER_ERROR
。
ts
import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .output (z .object ({greeting :z .string (),}),).query ((opts ) => {return {gre ,};}),});
ts
import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .output (z .object ({greeting :z .string (),}),).query ((opts ) => {return {gre ,};}),});
最基本的验证器:函数
您可以使用函数定义验证器,而无需任何第三方依赖项。
我们不建议您创建自定义验证器,除非您有特殊需要,但了解这里没有魔法很重要——它只是typescript!
在大多数情况下,我们建议您使用 验证库
ts
import {initTRPC } from '@trpc/server';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Input is not a string');}).output ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Output is not a string');}).query ((opts ) => {const {input } =opts ;return `hello ${input }`;}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Input is not a string');}).output ((value ): string => {if (typeofvalue === 'string') {returnvalue ;}throw newError ('Output is not a string');}).query ((opts ) => {const {input } =opts ;return `hello ${input }`;}),});export typeAppRouter = typeofappRouter ;
库集成
tRPC 开箱即用地支持许多流行的验证和解析库。以下是我们正式维护支持的验证器的一些使用示例。
使用 Zod
Zod 是我们的默认推荐,它拥有强大的生态系统,使其成为在代码库的多个部分中使用的绝佳选择。如果您没有自己的意见,并且想要一个不会限制未来需求的强大库,那么 Zod 是一个不错的选择。
ts
import {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).output (z .object ({greeting :z .string (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import {z } from 'zod';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (z .object ({name :z .string (),}),).output (z .object ({greeting :z .string (),}),).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 Yup
ts
import {initTRPC } from '@trpc/server';import * asyup from 'yup';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =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 typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import * asyup from 'yup';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =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 typeAppRouter = typeofappRouter ;
使用 Superstruct
ts
import {initTRPC } from '@trpc/server';import {object ,string } from 'superstruct';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (object ({name :string () })).output (object ({greeting :string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import {object ,string } from 'superstruct';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (object ({name :string () })).output (object ({greeting :string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 scale-ts
ts
import {initTRPC } from '@trpc/server';import * as$ from 'scale-codec';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ($ .object ($ .field ('name',$ .str ))).output ($ .object ($ .field ('greeting',$ .str ))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import * as$ from 'scale-codec';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input ($ .object ($ .field ('name',$ .str ))).output ($ .object ($ .field ('greeting',$ .str ))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 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;
使用 ArkType
ts
import {initTRPC } from '@trpc/server';import {type } from 'arktype';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (type ({name : 'string' }).assert ).output (type ({greeting : 'string' }).assert ).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import {type } from 'arktype';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (type ({name : 'string' }).assert ).output (type ({greeting : 'string' }).assert ).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 runtypes
ts
import {initTRPC } from '@trpc/server';import * asT from 'runtypes';constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (T .Record ({name :T .String })).output (T .Record ({greeting :T .String })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import * asT from 'runtypes';constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (T .Record ({name :T .String })).output (T .Record ({greeting :T .String })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 Valibot
ts
import {initTRPC } from '@trpc/server';import {wrap } from '@typeschema/valibot';import {object ,string } from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (wrap (object ({name :string () }))).output (wrap (object ({greeting :string () }))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
ts
import {initTRPC } from '@trpc/server';import {wrap } from '@typeschema/valibot';import {object ,string } from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (wrap (object ({name :string () }))).output (wrap (object ({greeting :string () }))).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
使用 @robolex/sure
如果需要,您可以定义自己的错误类型和错误抛出函数。为了方便起见,@robolex/sure
提供了 sure/src/err.ts
ts
// sure/src/err.tsexport const err = (schema) => (input) => {const [good, result] = schema(input);if (good) return result;throw result;};
ts
// sure/src/err.tsexport 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;
贡献您自己的验证器库
如果您正在开发支持 tRPC 用法的验证器库,请随时为本页打开一个 PR,其中包含与这里其他示例等效的使用方式,以及指向您文档的链接。
在大多数情况下,与 tRPC 的集成就像满足几个现有的类型接口一样简单,但在某些情况下,我们可能会接受 PR 来添加一个新的支持接口。请随时打开一个问题进行讨论。您可以在这里查看现有的支持接口