入力&出力バリデーター
このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →
tRPCプロシージャーは入力・出力の検証ロジックを定義でき、バリデーターは入力と出力の型推論にも使用されます(利用可能な場合Standard Schemaインターフェースを、それ以外の場合はサポート対象バリデーターのカスタムインターフェースを使用)。主要なバリデーターをファーストクラスでサポートしており、直接サポートしていないライブラリも統承可能です。
入力バリデーター
入力バリデーターを定義することで、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()は積み上げて複雑な型を構築でき、ミドルウェアで複数プロシージャーに共通の入力を適用する際に特に有用です。
tsconstbaseProcedure =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 }`,};}),});
tsconstbaseProcedure =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で応答します。
tsimport {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 ,};}),});
tsimport {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_だということです!
ほとんどの場合検証ライブラリの使用を推奨します
tsimport {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 ;
tsimport {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はStandard Schemaに準拠するライブラリを含む主要な検証・パースライブラリをすぐに利用できます。以下は公式サポート対象バリデーターの使用例です。
Zodの使用
Zodはデフォルト推奨ライブラリです。強力なエコシステムを持ち、コードベースの複数部分で使用するのに最適です。特定のこだわりがなく将来の制限もない強力なライブラリを求める場合、Zodは優れた選択肢です。
tsimport {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 ;
tsimport {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の使用
tsimport {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 ;
tsimport {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の使用
tsimport {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 ;
tsimport {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の使用
tsimport {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 ;
tsimport {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の使用
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;
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' })).query((opts) => {return {greeting: `hello ${opts.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' })).query((opts) => {return {greeting: `hello ${opts.input.name}`,};}),});export type AppRouter = typeof appRouter;
effectの使用
tsimport { 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;
tsimport { 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;
Valibotの使用
tsimport {initTRPC } from '@trpc/server';import * asv from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (v .object ({name :v .string () })).output (v .object ({greeting :v .string () })).query (({input }) => {return {greeting : `hello ${input .name }`,};}),});export typeAppRouter = typeofappRouter ;
tsimport {initTRPC } from '@trpc/server';import * asv from 'valibot';export constt =initTRPC .create ();constpublicProcedure =t .procedure ;export constappRouter =t .router ({hello :publicProcedure .input (v .object ({name :v .string () })).output (v .object ({greeting :v .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;};
tsimport { 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;
tsimport { 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;
TypeBoxを使用する場合
tsimport { 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;
tsimport { 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;
独自バリデータライブラリの提供
tRPC対応のバリデータライブラリを開発されている場合、このページに他の例と同等の使用法とドキュメントへのリンクを追加するPRを歓迎します。
tRPCとの統合は、既存の型インターフェースのいずれかに準拠するだけで実現できる場合がほとんどです。Standard Schemaへの準拠を推奨しますが、新しいインターフェースの追加提案についてPRを受け入れる場合もあります。議論のためにIssueを開いてください。既存のサポート対象インターフェースやパース/バリデーション関数はコード内で確認できます。