Fastify アダプター
サンプルアプリケーション
Fastify アダプターを使い始めるのに最適な方法は、サンプルアプリケーションを確認することです。
| 説明 | リンク |
|---|---|
|
Fastify で tRPC を使用する方法
依存関係のインストール
bashyarn add @trpc/server fastify zod
bashyarn add @trpc/server fastify zod
Zod は必須の依存関係ではありませんが、以下のサンプルルーターで使用されています。
ルーターの作成
まず最初に、クエリ、ミューテーション、およびサブスクリプションを処理するためのルーターが必要です。
サンプルルーターを以下に示します。router.ts という名前のファイルに保存してください。
router.ts
router.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
router.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
ルーターファイルが大きくなりすぎたら、ルーターを複数のサブルーターに分割し、それぞれを独自のファイルに実装してください。次に、それらをマージして、単一のルート appRouter にします。
コンテキストの作成
次に、各リクエストに対して作成されるコンテキストが必要です。
サンプルコンテキストを以下に示します。context.ts という名前のファイルに保存してください。
context.ts
context.tstsimport { 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.tstsimport { 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.tstsimport {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 monitoringconsole.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.tstsimport {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 monitoringconsole.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 |
|---|---|
getUser | GET https://:3000/trpc/getUserById?input=INPUT INPUT は URI エンコードされた JSON 文字列です。 |
createUser | POST https://:3000/trpc/createUser req.body の型は User |
サブスクリプション(WebSocket)を有効にする方法
Fastify アダプターは、@fastify/websocket プラグインを介してサブスクリプションをサポートします。上記の手順に加えて、依存関係をインストールし、ルーターにいくつかのサブスクリプションを追加し、プラグインの useWSS オプションをアクティブにするだけで済みます。@fastify/websocket に必要な最小 Fastify バージョンは 3.11.0 です。
依存関係のインストール
bashyarn add @fastify/websocket
bashyarn add @fastify/websocket
@fastify/websocket のインポートと登録
tsimport ws from '@fastify/websocket';server.register(ws);
tsimport ws from '@fastify/websocket';server.register(ws);
いくつかのサブスクリプションを追加
前の手順で作成した router.ts ファイルを編集し、次のコードを追加します。
router.tstsimport { 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.tstsimport { 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.tstsserver.register(fastifyTRPCPlugin, {useWSS: true,// ...});
server.tstsserver.register(fastifyTRPCPlugin, {useWSS: true,// ...});
大丈夫です。トピック randomNumber をサブスクライブすると、1 秒ごとに乱数が届くはずです🚀。
Fastify プラグインオプション
| 名前 | 型 | オプション | デフォルト | 説明 |
|---|---|---|---|---|
| prefix | string | true | "/trpc" | |
| useWSS | boolean | true | false | |
| trpcOptions | NodeHTTPHandlerOptions | false | n/a |