adapter-fastify
Mount tRPC as a Fastify plugin with fastifyTRPCPlugin from @trpc/server/adapters/fastify. Configure prefix, trpcOptions (router, createContext, onError). Enable WebSocket subscriptions with useWSS and @fastify/websocket. Set routerOptions.maxParamLength for batch requests. Requires Fastify v5+. FastifyTRPCPluginOptions for type-safe onError. CreateFastifyContextOptions provides req, res. Register @fastify/websocket before the tRPC plugin.
Fastify Adapter
From https://trpc.io/docs/server/adapters/fastify
Example app
The best way to start with the Fastify adapter is to take a look at the example application.
| Description | Links |
|---|---|
|
How to use tRPC with Fastify
Install dependencies
yarn add @trpc/server fastify zod⚠️ Fastify version requirement
The tRPC v11 Fastify adapter requires Fastify v5+. Using Fastify v4 may cause requests to return empty responses without errors.
Zod isn't a required dependency, but it's used in the sample router below.
Create the router
First of all you need a router to handle your queries, mutations and subscriptions.
A sample router is given below, save it in a file named router.ts.
router.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;If your router file starts getting too big, split your router into several subrouters each implemented in its own file. Then merge them into a single root appRouter.
Create the context
Then you need a context that will be created for each request.
A sample context is given below, save it in a file named context.ts:
context.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>>;Create Fastify server
tRPC includes an adapter for Fastify out of the box. This adapter lets you convert your tRPC router into a Fastify plugin. In order to prevent errors during large batch requests, make sure to set the maxParamLength Fastify option to a suitable value, as shown.
Tip
Due to limitations in Fastify's plugin system and type inference, there might be some issues getting for example onError typed correctly. You can add a satisfies FastifyTRPCPluginOptions<AppRouter>['trpcOptions'] to help TypeScript out and get the correct types.
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);
}
})();Your endpoints are now available via HTTP!
| Endpoint | HTTP URI |
|---|---|
| getUserById | GET http://localhost:3000/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
| createUser | POST http://localhost:3000/trpc/createUser with req.body of type User |
Enable WebSockets
The Fastify adapter supports WebSockets via the @fastify/websocket plugin. All you have to do in addition to the above steps is install the dependency, add some subscriptions to your router, and activate the useWSS option in the plugin. The minimum @fastify/websocket version required is 3.11.0.
Install dependencies
yarn add @fastify/websocketImport and register @fastify/websocket
import ws from '@fastify/websocket';
server.register(ws);Add some subscriptions
Edit the router.ts file created in the previous steps and add the following code:
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));
}
}),
});Activate the useWSS option
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,
},
},
});You can now subscribe to the randomNumber topic and should receive a random number every second 🚀.
Fastify plugin options
| name | type | optional | default | description |
|---|---|---|---|---|
| prefix | string | true | "/trpc" | URL prefix for tRPC routes |
| useWSS | boolean | true | false | Enable WebSocket support via @fastify/websocket |
| trpcOptions | FastifyHandlerOptions<AppRouter, Request, Reply> | false | n/a | tRPC handler options including router, createContext, etc. |
trpcOptions accepts the options every adapter takes, like onError and maxBatchSize. With useWSS, it also accepts the WebSocket options keepAlive, dangerouslyDisablePong and experimental_encoder.
Common mistakes
- Registering @fastify/websocket after the tRPC plugin. It only adds WebSocket support to routes registered after it, so register it first.
- Leaving maxParamLength at Fastify's default of 100. A batch request puts every procedure in one path parameter, like /trpc/getUserById,createUser, and Fastify answers 414 URI Too Long once that gets longer than maxParamLength.
- Using Fastify v4. The tRPC v11 adapter requires Fastify v5 or later.
Common options
Every HTTP adapter accepts these options, next to its own (like endpoint for Fetch, or basePath for Standalone):
| Option | Description |
|---|---|
| router | Your app router. Required. |
| createContext | Creates the context for each request. It receives the adapter's own objects (e.g. req and res) plus info. |
| onError | Called with { error, type, path, input, ctx, req } for every error, e.g. to report it. See Error handling. |
| responseMeta | Returns { status, headers } for the response, e.g. for caching. |
| allowBatching | Set to false to reject batch requests. Defaults to true. |
| maxBatchSize | Rejects batch requests with more calls than this with a 400 Bad Request. Set maxItems on your batch link to the same or a lower value. |
| allowMethodOverride | Accepts queries sent as POST, for clients that set methodOverride: 'POST' on their link. Defaults to false. |
The Node.js adapters (Standalone, Express and Next.js) also accept:
- maxBodySize: the largest request body in bytes; larger ones are rejected with 413 PAYLOAD_TOO_LARGE.
- middleware: a connect-style (req, res, next) function that runs before tRPC, e.g. cors().
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { appRouter } from './router';
createHTTPServer({
router: appRouter,
createContext: () => ({}),
onError({ error, path }) {
console.error(`tRPC error on '${path}':`, error);
},
maxBatchSize: 10,
maxBodySize: 1_000_000, // 1 MB
}).listen(3000);From https://trpc.io/docs/server/adapters#common-options
Related skills
- server-setup (https://trpc.io/docs/server/overview): npx @tanstack/intent@latest load @trpc/server#server-setup