@trpc/server
Version:
314 lines (239 loc) • 12.2 kB
Markdown
---
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`