본문으로 건너뛰기
버전: 11.x

Fastify 어댑터

예제 앱

Fastify 어댑터를 시작하는 가장 좋은 방법은 예제 애플리케이션을 살펴보는 것입니다.

설명링크
  • WebSocket이 포함된 Fastify 서버
  • node에서 간단한 tRPC 클라이언트

Fastify와 함께 tRPC 사용 방법

의존성 설치

bash
yarn add @trpc/server fastify zod
bash
yarn add @trpc/server fastify zod

⚠️ Fastify 버전 요구 사항

tRPC v11 Fastify 어댑터에는 Fastify v5 이상이 필요합니다. Fastify v4를 사용하면 오류 없이 요청이 빈 응답으로 반환될 수 있습니다.

Zod는 필수 의존성이 아니지만, 아래 샘플 라우터에서 사용됩니다.

AI 에이전트

AI 코딩 에이전트를 사용하는 경우, 더 나은 코드 생성을 위해 tRPC 스킬을 설치하세요:

bash
npx @tanstack/intent@latest install
bash
npx @tanstack/intent@latest install

라우터 생성

우선 쿼리, 뮤테이션, 구독을 처리하기 위한 라우터가 필요합니다.

아래에 샘플 라우터가 제공되며, router.ts이라는 이름의 파일로 저장하세요.

router.ts
router.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
type User = {
id: string;
name: string;
bio?: string;
};
 
const users: Record<string, User> = {};
 
export const t = initTRPC.create();
 
export const appRouter = t.router({
getUserById: t.procedure.input(z.string()).query((opts) => {
return users[opts.input]; // input type is string
}),
createUser: t.procedure
.input(
z.object({
name: z.string().min(3),
bio: z.string().max(142).optional(),
}),
)
.mutation((opts) => {
const id = Date.now().toString();
const user: User = { id, ...opts.input };
users[user.id] = user;
return user;
}),
});
 
// export type definition of API
export type AppRouter = typeof appRouter;
router.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
 
type User = {
id: string;
name: string;
bio?: string;
};
 
const users: Record<string, User> = {};
 
export const t = initTRPC.create();
 
export const appRouter = t.router({
getUserById: t.procedure.input(z.string()).query((opts) => {
return users[opts.input]; // input type is string
}),
createUser: t.procedure
.input(
z.object({
name: z.string().min(3),
bio: z.string().max(142).optional(),
}),
)
.mutation((opts) => {
const id = Date.now().toString();
const user: User = { id, ...opts.input };
users[user.id] = user;
return user;
}),
});
 
// export type definition of API
export type AppRouter = typeof appRouter;

라우터 파일이 너무 커지면, 각 서브라우터를 별도의 파일로 구현하여 여러 개의 서브라우터로 분리하세요. 그런 다음 병합하여 단일 루트 appRouter로 만드세요.

컨텍스트 생성

다음으로 각 요청마다 생성될 컨텍스트가 필요합니다.

아래에 샘플 컨텍스트가 제공되며, context.ts라는 이름의 파일로 저장하세요:

context.ts
context.ts
ts
import { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
 
export function createContext({ req, res }: CreateFastifyContextOptions) {
const user = { name: req.headers.username ?? 'anonymous' };
 
return { req, res, user };
}
 
export type Context = Awaited<ReturnType<typeof createContext>>;
context.ts
ts
import { CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';
 
export function createContext({ req, res }: CreateFastifyContextOptions) {
const user = { name: req.headers.username ?? 'anonymous' };
 
return { req, res, user };
}
 
export type Context = Awaited<ReturnType<typeof createContext>>;

Fastify 서버 생성

tRPC에는 Fastify용 어댑터가 기본으로 포함되어 있습니다. 이 어댑터를 사용하면 tRPC 라우터를 Fastify 플러그인으로 변환할 수 있습니다. 대규모 배치 요청 시 오류를 방지하려면, 아래에 표시된 대로 maxParamLength Fastify 옵션을 적절한 값으로 설정하세요.

Fastify의 플러그인 시스템과 타입 추론의 제한 사항으로 인해, 예를 들어 onError의 타입이 올바르게 지정되지 않는 문제가 발생할 수 있습니다. TypeScript를 돕고 올바른 타입을 얻기 위해 satisfies FastifyTRPCPluginOptions<AppRouter>['trpcOptions']을 추가할 수 있습니다.

server.ts
ts
import {
fastifyTRPCPlugin,
FastifyTRPCPluginOptions,
} from '@trpc/server/adapters/fastify';
import fastify from 'fastify';
import { createContext } from './context';
import { appRouter, type AppRouter } from './router';
 
const server = fastify({
routerOptions: {
maxParamLength: 5000,
},
});
 
server.register(fastifyTRPCPlugin, {
prefix: '/trpc',
trpcOptions: {
router: appRouter,
createContext,
onError({ path, error }) {
// report to error monitoring
console.error(`Error in tRPC handler on path '${path}':`, error);
},
} satisfies FastifyTRPCPluginOptions<AppRouter>['trpcOptions'],
});
 
(async () => {
try {
await server.listen({ port: 3000 });
} catch (err) {
server.log.error(err);
process.exit(1);
}
})();
server.ts
ts
import {
fastifyTRPCPlugin,
FastifyTRPCPluginOptions,
} from '@trpc/server/adapters/fastify';
import fastify from 'fastify';
import { createContext } from './context';
import { appRouter, type AppRouter } from './router';
 
const server = fastify({
routerOptions: {
maxParamLength: 5000,
},
});
 
server.register(fastifyTRPCPlugin, {
prefix: '/trpc',
trpcOptions: {
router: appRouter,
createContext,
onError({ path, error }) {
// report to error monitoring
console.error(`Error in tRPC handler on path '${path}':`, error);
},
} satisfies FastifyTRPCPluginOptions<AppRouter>['trpcOptions'],
});
 
(async () => {
try {
await server.listen({ port: 3000 });
} catch (err) {
server.log.error(err);
process.exit(1);
}
})();

이제 엔드포인트가 HTTP를 통해 사용할 수 있습니다!

엔드포인트HTTP URI
getUserByIdGET http://localhost:3000/trpc/getUserById?input=INPUT

여기서 INPUT은 URI 인코딩된 JSON 문자열입니다.
createUserPOST http://localhost:3000/trpc/createUser

여기서 req.body의 타입은 User입니다

WebSocket 활성화

Fastify 어댑터는 @fastify/websocket 플러그인을 통해 WebSockets를 지원합니다. 위의 단계 외에도 종속성을 설치하고 라우터에 구독을 추가하며 플러그인에서 useWSS 옵션을 활성화하기만 하면 됩니다. 요구되는 최소 @fastify/websocket 버전은 3.11.0입니다.

종속성 설치

bash
yarn add @fastify/websocket
bash
yarn add @fastify/websocket

@fastify/websocket 가져오기 및 등록

ts
import ws from '@fastify/websocket';
 
server.register(ws);
ts
import ws from '@fastify/websocket';
 
server.register(ws);

구독 추가

이전 단계에서 생성된 router.ts 파일을 편집하고 다음 코드를 추가하세요:

router.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const appRouter = t.router({
randomNumber: t.procedure.subscription(async function* () {
while (true) {
yield { randomNumber: Math.random() };
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}),
});
router.ts
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC.create();
 
export const appRouter = t.router({
randomNumber: t.procedure.subscription(async function* () {
while (true) {
yield { randomNumber: Math.random() };
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}),
});

useWSS 옵션 활성화

server.ts
ts
import {
fastifyTRPCPlugin,
FastifyTRPCPluginOptions,
} from '@trpc/server/adapters/fastify';
import fastify from 'fastify';
import { createContext } from './context';
import { appRouter, type AppRouter } from './router';
 
const server = fastify();
 
server.register(fastifyTRPCPlugin, {
useWSS: true,
trpcOptions: {
router: appRouter,
createContext,
// Enable heartbeat messages to keep connection open (disabled by default)
keepAlive: {
enabled: true,
// server ping message interval in milliseconds
pingMs: 30000,
// connection is terminated if pong message is not received in this many milliseconds
pongWaitMs: 5000,
},
},
});
server.ts
ts
import {
fastifyTRPCPlugin,
FastifyTRPCPluginOptions,
} from '@trpc/server/adapters/fastify';
import fastify from 'fastify';
import { createContext } from './context';
import { appRouter, type AppRouter } from './router';
 
const server = fastify();
 
server.register(fastifyTRPCPlugin, {
useWSS: true,
trpcOptions: {
router: appRouter,
createContext,
// Enable heartbeat messages to keep connection open (disabled by default)
keepAlive: {
enabled: true,
// server ping message interval in milliseconds
pingMs: 30000,
// connection is terminated if pong message is not received in this many milliseconds
pongWaitMs: 5000,
},
},
});

이제 randomNumber 주제에 구독할 수 있으며, 매초 무작위 숫자를 수신하게 됩니다 🚀.

Fastify 플러그인 옵션

이름타입선택 사항기본값설명
prefixstringtrue"/trpc"tRPC 라우트의 URL 접두사
useWSSbooleantruefalse@fastify/websocket을 통해 WebSocket 지원 활성화
trpcOptionsFastifyHandlerOptions<AppRouter, Request, Reply>falsen/arouter, createContext 등을 포함하는 tRPC 핸들러 옵션