Middlewares
非公式ベータ版翻訳
このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →
middleware()メソッドを使用して、ルーター全体にミドルウェアを追加できます。ミドルウェアはプロシージャの呼び出しをラップし、その戻り値を必ず通過させる必要があります。
認可
以下の例では、admin.*への呼び出しは、クエリやミューテーションを実行する前にユーザーが「admin」であることを確認します。
tstrpc.router<Context>().query('foo', {resolve() {return 'bar';},}).merge('admin.',trpc.router<Context>().middleware(async (opts) => {if (!opts.ctx.user?.isAdmin) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next();}).query('secretPlace', {resolve() {return 'a key';},}),);
tstrpc.router<Context>().query('foo', {resolve() {return 'bar';},}).merge('admin.',trpc.router<Context>().middleware(async (opts) => {if (!opts.ctx.user?.isAdmin) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next();}).query('secretPlace', {resolve() {return 'a key';},}),);
ヒント
上記の例でスローされるTRPCErrorの詳細については、エラーハンドリングを参照してください。
ロギング
以下の例では、クエリの実行時間が自動的にログ記録されます。
tstrpc.router<Context>().middleware(async ({ path, type, next }) => {const start = Date.now();const result = await next();const durationMs = Date.now() - start;result.ok? logMock('OK request timing:', { path, type, durationMs }): logMock('Non-OK request timing', { path, type, durationMs });return result;}).query('foo', {resolve() {return 'bar';},}).query('abc', {resolve() {return 'def';},});
tstrpc.router<Context>().middleware(async ({ path, type, next }) => {const start = Date.now();const result = await next();const durationMs = Date.now() - start;result.ok? logMock('OK request timing:', { path, type, durationMs }): logMock('Non-OK request timing', { path, type, durationMs });return result;}).query('foo', {resolve() {return 'bar';},}).query('abc', {resolve() {return 'def';},});
コンテキストのスワップ
ミドルウェアはルーターのコンテキストを置換でき、下流のプロシージャは新しいコンテキスト値を受け取ります:
tsinterface Context {// user is nullableuser?: {id: string;};}trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,user: opts.ctx.user, // user value is known to be non-null now},});}).query('userId', {async resolve({ ctx }) {return ctx.user.id;},});
tsinterface Context {// user is nullableuser?: {id: string;};}trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,user: opts.ctx.user, // user value is known to be non-null now},});}).query('userId', {async resolve({ ctx }) {return ctx.user.id;},});
createProtectedRouter()ヘルパー
このヘルパーはアプリツリーの任意の場所で使用でき、下流のプロシージャに認可を強制します。
server/createRouter.tstsximport * as trpc from '@trpc/server';import { Context } from './context';export function createProtectedRouter() {return trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new trpc.TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,// infers that `user` is non-nullable to downstream proceduresuser: opts.ctx.user,},});});}
server/createRouter.tstsximport * as trpc from '@trpc/server';import { Context } from './context';export function createProtectedRouter() {return trpc.router<Context>().middleware((opts) => {if (!opts.ctx.user) {throw new trpc.TRPCError({ code: 'UNAUTHORIZED' });}return opts.next({ctx: {...opts.ctx,// infers that `user` is non-nullable to downstream proceduresuser: opts.ctx.user,},});});}
生の入力
ミドルウェアはプロシージャに渡される生の入力にアクセスできます。これはプロシージャ入力へのアクセスを必要とする認証やその他の前処理に使用でき、コンテキストスワップと組み合わせて使用すると特に有用です。
注意
ミドルウェアに渡されるrawInputは、プロシージャのinputスキーマ/バリデータによる検証がまだ行われていません。使用時には注意してください!このため、rawInputはunknown型を持ちます。詳細は#1059を参照してください。
tsconst inputSchema = z.object({ userId: z.string() });trpc.router<Context>().middleware(async ({ next, rawInput, ctx }) => {const result = inputSchema.safeParse(rawInput);if (!result.success) throw new TRPCError({ code: 'BAD_REQUEST' });const { userId } = result.data;// Check user id authreturn next({ ctx: { ...ctx, userId } });}).query('userId', {input: inputSchema,resolve({ ctx }) {return ctx.userId;},});
tsconst inputSchema = z.object({ userId: z.string() });trpc.router<Context>().middleware(async ({ next, rawInput, ctx }) => {const result = inputSchema.safeParse(rawInput);if (!result.success) throw new TRPCError({ code: 'BAD_REQUEST' });const { userId } = result.data;// Check user id authreturn next({ ctx: { ...ctx, userId } });}).query('userId', {input: inputSchema,resolve({ ctx }) {return ctx.userId;},});