メインコンテンツへスキップ
バージョン: 11.x

Fastifyアダプター

非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

サンプルアプリケーション

Fastifyアダプターを使い始める最良の方法は、サンプルアプリケーションを参照することです。

DescriptionLinks
  • Fastify server with WebSocket
  • Simple tRPC client in node

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プラグインに変換できます。大規模なバッチリクエスト中のエラーを防ぐため、図のようにFastifyオプションmaxParamLengthに適切な値を設定してください。

ヒント

Fastifyのプラグインシステムと型推論の制限により、onErrorなどの型を正しく取得できない問題が発生する可能性があります。satisfies FastifyTRPCPluginOptions<AppRouter>['trpcOptions']を追加することで、TypeScriptを支援し正しい型を取得できます。

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経由で利用可能になりました!

EndpointHTTP URI
getUserByIdGET http://localhost:3000/trpc/getUserById?input=INPUT

where INPUT is a URI-encoded JSON string.
createUserPOST http://localhost:3000/trpc/createUser

with req.body of type 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プラグインオプション

nametypeoptionaldefaultdescription
prefixstringtrue"/trpc"URL prefix for tRPC routes
useWSSbooleantruefalseEnable WebSocket support via @fastify/websocket
trpcOptionsFastifyHandlerOptions<AppRouter, Request, Reply>falsen/atRPC handler options including router, createContext, etc.