미들웨어
비공식 베타 번역
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
middleware() 메서드를 사용하면 전체 라우터에 미들웨어를 추가할 수 있습니다. 미들웨어는 프로시저 호출을 감싸고 반드시 해당 반환값을 전달해야 합니다.
인증
아래 예제에서 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,},});});}
원본 입력값
미들웨어는 프로시저에 전달될 원본 입력값(raw input)에 접근할 수 있습니다. 이는 프로시저 입력값에 대한 접근이 필요한 인증 또는 다른 전처리를 위해 사용될 수 있으며, 컨텍스트 교체와 함께 사용될 때 특히 유용합니다.
주의
미들웨어에 전달된 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;},});