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

Fastify アダプター

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

Fastify アダプターを使い始めるのに最適な方法は、サンプルアプリケーションを確認することです。

説明リンク
  • WebSocket を使用した Fastify サーバー
  • Node でのシンプルな tRPC クライアント

Fastify で tRPC を使用する方法

依存関係のインストール

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

Zod は必須の依存関係ではありませんが、以下のサンプルルーターで使用されています。

ルーターの作成

まず最初に、クエリ、ミューテーション、およびサブスクリプションを処理するためのルーターが必要です。

サンプルルーターを以下に示します。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({
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({
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
getUserGET http://localhost:3000/trpc/getUserById?input=INPUT

INPUT は URI エンコードされた JSON 文字列です。
createUserPOST http://localhost:3000/trpc/createUser

req.body の型は User

サブスクリプション(WebSocket)を有効にする方法

Fastify アダプターは、@fastify/websocket プラグインを介してサブスクリプションをサポートします。上記の手順に加えて、依存関係をインストールし、ルーターにいくつかのサブスクリプションを追加し、プラグインの useWSS オプションをアクティブにするだけで済みます。@fastify/websocket に必要な最小 Fastify バージョンは 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';
import { observable } from '@trpc/server/observable';
const t = initTRPC.create();
export const appRouter = t.router({
randomNumber: t.procedure.subscription(() => {
return observable<{ randomNumber: number }>((emit) => {
const timer = setInterval(() => {
emit.next({ randomNumber: Math.random() });
}, 1000);
return () => {
clearInterval(timer);
};
});
}),
});
router.ts
ts
import { initTRPC } from '@trpc/server';
import { observable } from '@trpc/server/observable';
const t = initTRPC.create();
export const appRouter = t.router({
randomNumber: t.procedure.subscription(() => {
return observable<{ randomNumber: number }>((emit) => {
const timer = setInterval(() => {
emit.next({ randomNumber: Math.random() });
}, 1000);
return () => {
clearInterval(timer);
};
});
}),
});

useWSS オプションのアクティブ化

server.ts
ts
server.register(fastifyTRPCPlugin, {
useWSS: true,
// ...
});
server.ts
ts
server.register(fastifyTRPCPlugin, {
useWSS: true,
// ...
});

大丈夫です。トピック randomNumber をサブスクライブすると、1 秒ごとに乱数が届くはずです🚀。

Fastify プラグインオプション

名前オプションデフォルト説明
prefixstringtrue"/trpc"
useWSSbooleantruefalse
trpcOptionsNodeHTTPHandlerOptionsfalsen/a