---
name: adapter-fastify
description: 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.
requires:
  - server-setup
sources:
  - www/v11/docs/server/adapters/fastify.md
  - www/v11/docs/server/adapters.md
metadata:
  type: core
  library: trpc
  library_version: 11.23.0
---

<!-- Generated from www/v11/docs/server/adapters/fastify.md by www/build/skills.ts. Edit the docs instead. -->

# 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.

<table>
  <thead>
    <tr>
      <th>Description</th>
      <th>Links</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>
        <ul>
          <li>Fastify server with WebSocket</li>
          <li>Simple tRPC client in node</li>
        </ul>
      </td>
      <td>
        <ul>
          <li><a href="https://codesandbox.io/s/github/trpc/trpc/tree/main/examples/fastify-server">CodeSandbox</a></li>
          <li><a href="https://github.com/trpc/trpc/tree/main/examples/fastify-server">Source</a></li>
        </ul>
      </td>
    </tr>
  </tbody>
</table>

## How to use tRPC with Fastify

### Install dependencies

```bash
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](https://github.com/colinhacks/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](https://trpc.io/docs/server/routers) to handle your queries, mutations and subscriptions.

A sample router is given below, save it in a file named `router.ts`.

<details>
  <summary>router.ts</summary>

```ts no-check title="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;
```

</details>

If your router file starts getting too big, split your router into several subrouters each implemented in its own file. Then [merge them](https://trpc.io/docs/server/merging-routers) into a single root `appRouter`.

### Create the context

Then you need a [context](https://trpc.io/docs/server/context) that will be created for each request.

A sample context is given below, save it in a file named `context.ts`:

<details>
  <summary>context.ts</summary>

```ts no-check title="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>>;
```

</details>

### Create Fastify server

tRPC includes an adapter for [Fastify](https://www.fastify.io/) out of the box. This adapter lets you convert your tRPC router into a [Fastify plugin](https://www.fastify.io/docs/latest/Reference/Plugins/). 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.


```ts no-check title="server.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);
  }
})();
```

Your endpoints are now available via HTTP!

| Endpoint      | HTTP URI                                                                                                       |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| `getUserById` | `GET http://localhost:3000/trpc/getUserById?input=INPUT` <br/><br/>where `INPUT` is a URI-encoded JSON string. |
| `createUser`  | `POST http://localhost:3000/trpc/createUser` <br/><br/>with `req.body` of type `User`                          |

## Enable WebSockets

The Fastify adapter supports [WebSockets](https://trpc.io/docs/server/websockets) via the [@fastify/websocket](https://www.npmjs.com/package/@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](https://trpc.io/docs/server/adapters/fastify#fastify-plugin-options) in the plugin. The minimum `@fastify/websocket` version required is `3.11.0`.

### Install dependencies

```bash
yarn add @fastify/websocket
```

### Import and register `@fastify/websocket`

```ts no-check
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:

```ts no-check title="router.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));
    }
  }),
});
```

### Activate the `useWSS` option

```ts no-check title="server.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,
    },
  },
});
```

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](https://trpc.io/docs/server/adapters#common-options), 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](https://trpc.io/docs/server/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](https://trpc.io/docs/server/error-handling#handling-errors). |
| `responseMeta`        | Returns `{ status, headers }` for the response, e.g. for [caching](https://trpc.io/docs/server/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()`](https://trpc.io/docs/server/adapters/standalone#handling-cors--options).

```ts no-check title="server.ts"
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`
