UNPKG

@trpc/server

Version:

The tRPC server library

322 lines (243 loc) • 13.2 kB
--- 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.25.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`. There's no limit by default; see [Request body size](https://trpc.io/docs/server/adapters#request-body-size). - `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: 1024 * 1024, // 1 MiB }).listen(3000); ``` ### Request body size The Node.js adapters read the request body into memory, and `maxBodySize` is unset by default, so they accept bodies of any size. On a server that's reachable from the internet, set `maxBodySize` so a single large request can't exhaust the process's memory. A body over the limit is rejected with `413 PAYLOAD_TOO_LARGE` as soon as it passes the limit, before it's parsed. We recommend `1024 * 1024` (1 MiB) for typical JSON inputs, as in the example above. If you accept files or other large inputs, set it a little above the largest one you expect, and keep it under about `10 * 1024 * 1024` (10 MiB). `maxBodySize` only applies to bodies tRPC reads itself. If a body parser such as `express.json()` or the Next.js API routes body parser has already set `req.body`, limit the size in that parser instead. A limit in your reverse proxy (like nginx's `client_max_body_size`) adds another layer. 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`