Fastify Adapter
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
bashyarn add @trpc/server fastify zod
bashyarn 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.
If you use an AI coding agent, install tRPC skills for better code generation:
bashnpx @tanstack/intent@latest install
bashnpx @tanstack/intent@latest install
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
router.tstsimport {initTRPC } from '@trpc/server';import {z } from 'zod';typeUser = {id : string;name : string;bio ?: string;};constusers :Record <string,User > = {};export constt =initTRPC .create ();export constappRouter =t .router ({getUserById :t .procedure .input (z .string ()).query ((opts ) => {returnusers [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 ) => {constid =Date .now ().toString ();constuser :User = {id , ...opts .input };users [user .id ] =user ;returnuser ;}),});// export type definition of APIexport typeAppRouter = typeofappRouter ;
router.tstsimport {initTRPC } from '@trpc/server';import {z } from 'zod';typeUser = {id : string;name : string;bio ?: string;};constusers :Record <string,User > = {};export constt =initTRPC .create ();export constappRouter =t .router ({getUserById :t .procedure .input (z .string ()).query ((opts ) => {returnusers [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 ) => {constid =Date .now ().toString ();constuser :User = {id , ...opts .input };users [user .id ] =user ;returnuser ;}),});// export type definition of APIexport typeAppRouter = typeofappRouter ;
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
context.tstsimport {CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';export functioncreateContext ({req ,res }:CreateFastifyContextOptions ) {constuser = {name :req .headers .username ?? 'anonymous' };return {req ,res ,user };}export typeContext =Awaited <ReturnType <typeofcreateContext >>;
context.tstsimport {CreateFastifyContextOptions } from '@trpc/server/adapters/fastify';export functioncreateContext ({req ,res }:CreateFastifyContextOptions ) {constuser = {name :req .headers .username ?? 'anonymous' };return {req ,res ,user };}export typeContext =Awaited <ReturnType <typeofcreateContext >>;
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.
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.
server.tstsimport {fastifyTRPCPlugin ,FastifyTRPCPluginOptions ,} from '@trpc/server/adapters/fastify';importfastify from 'fastify';import {createContext } from './context';import {appRouter , typeAppRouter } from './router';constserver =fastify ({routerOptions : {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 );},} satisfiesFastifyTRPCPluginOptions <AppRouter >['trpcOptions'],});(async () => {try {awaitserver .listen ({port : 3000 });} catch (err ) {server .log .error (err );process .exit (1);}})();
server.tstsimport {fastifyTRPCPlugin ,FastifyTRPCPluginOptions ,} from '@trpc/server/adapters/fastify';importfastify from 'fastify';import {createContext } from './context';import {appRouter , typeAppRouter } from './router';constserver =fastify ({routerOptions : {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 );},} satisfiesFastifyTRPCPluginOptions <AppRouter >['trpcOptions'],});(async () => {try {awaitserver .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
bashyarn add @fastify/websocket
bashyarn add @fastify/websocket
Import and register @fastify/websocket
tsimportws from '@fastify/websocket';server .register (ws );
tsimportws from '@fastify/websocket';server .register (ws );
Add some subscriptions
Edit the router.ts file created in the previous steps and add the following code:
router.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constappRouter =t .router ({randomNumber :t .procedure .subscription (async function* () {while (true) {yield {randomNumber :Math .random () };await newPromise ((resolve ) =>setTimeout (resolve , 1000));}}),});
router.tstsimport {initTRPC } from '@trpc/server';constt =initTRPC .create ();export constappRouter =t .router ({randomNumber :t .procedure .subscription (async function* () {while (true) {yield {randomNumber :Math .random () };await newPromise ((resolve ) =>setTimeout (resolve , 1000));}}),});
Activate the useWSS option
server.tstsimport {fastifyTRPCPlugin ,FastifyTRPCPluginOptions ,} from '@trpc/server/adapters/fastify';importfastify from 'fastify';import {createContext } from './context';import {appRouter , typeAppRouter } from './router';constserver =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 millisecondspingMs : 30000,// connection is terminated if pong message is not received in this many millisecondspongWaitMs : 5000,},},});
server.tstsimport {fastifyTRPCPlugin ,FastifyTRPCPluginOptions ,} from '@trpc/server/adapters/fastify';importfastify from 'fastify';import {createContext } from './context';import {appRouter , typeAppRouter } from './router';constserver =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 millisecondspingMs : 30000,// connection is terminated if pong message is not received in this many millisecondspongWaitMs : 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. |