UNPKG

@trpc/server

Version:

The tRPC server library

289 lines (220 loc) • 11.4 kB
--- name: adapter-standalone description: Mount tRPC on Node.js built-in HTTP server with createHTTPServer() from @trpc/server/adapters/standalone, createHTTPHandler() for custom http.createServer, createHTTP2Handler() for HTTP/2 with TLS. Configure basePath to slice URL prefix, CORS via the cors npm package passed as middleware option. CreateHTTPContextOptions provides req and res for context creation. Pass the common adapter options (onError, maxBatchSize, maxBodySize) to createHTTPServer. requires: - server-setup sources: - www/v11/docs/server/adapters/standalone.md - www/v11/docs/server/adapters.md metadata: type: core library: trpc library_version: 11.23.0 --- <!-- Generated from www/v11/docs/server/adapters/standalone.md by www/build/skills.ts. Edit the docs instead. --> # Standalone Adapter From https://trpc.io/docs/server/adapters/standalone tRPC's Standalone Adapter is the simplest way to get a new project working. It's ideal for local development, and for server-based production environments. In essence it's just a wrapper around the standard [Node.js HTTP Server](https://nodejs.org/api/http.html) with the normal options related to tRPC. If you have an existing API deployment like [Express](https://trpc.io/docs/server/adapters/express), [Fastify](https://trpc.io/docs/server/adapters/fastify), or [Next.js](https://trpc.io/docs/server/adapters/nextjs), which you want to integrate tRPC into, you should have a look at their respective adapters. Likewise if you have a preference to host on serverless or edge compute, we have adapters like [AWS Lambda](https://trpc.io/docs/server/adapters/aws-lambda) and [Fetch](https://trpc.io/docs/server/adapters/fetch) which may fit your needs. It's also not uncommon, where the deployed adapter is hard to run on local machines, to have 2 entry-points in your application. You could use the Standalone Adapter for local development, and a different adapter when deployed. ## Example app <table> <thead> <tr> <th>Description</th> <th>Links</th> </tr> </thead> <tbody> <tr> <td>Standalone tRPC Server</td> <td> <ul> <li><a href="https://stackblitz.com/github/trpc/trpc/tree/main/examples/minimal">StackBlitz</a></li> <li><a href="https://github.com/trpc/trpc/blob/main/examples/minimal/src/server/index.ts">Source</a></li> </ul> </td> </tr> <tr> <td>Standalone tRPC Server with CORS handling</td> <td> <ul> <li><a href="https://stackblitz.com/github/trpc/trpc/tree/main/examples/minimal-react">StackBlitz</a></li> <li><a href="https://github.com/trpc/trpc/blob/main/examples/minimal-react/server/index.ts">Source</a></li> </ul> </td> </tr> </tbody> </table> ## Setting up a Standalone tRPC Server ### 1. Implement your App Router Implement your tRPC router. For example: ```ts no-check title="appRouter.ts" import { initTRPC } from '@trpc/server'; import { z } from 'zod'; export const t = initTRPC.create(); export const appRouter = t.router({ getUser: t.procedure.input(z.string()).query((opts) => { return { id: opts.input, name: 'Bilbo' }; }), createUser: t.procedure .input(z.object({ name: z.string().min(5) })) .mutation(async (opts) => { // use your ORM of choice return { id: '1', ...opts.input }; }), }); // export type definition of API export type AppRouter = typeof appRouter; ``` For more information, you can look at the [quickstart guide](https://trpc.io/docs/quickstart) ### 2. Use the Standalone adapter The Standalone adapter runs a simple Node.js HTTP server. ```ts no-check title="server.ts" import { createHTTPServer } from '@trpc/server/adapters/standalone'; import { appRouter } from './appRouter'; createHTTPServer({ router: appRouter, createContext() { return {}; }, // basePath: '/trpc/', // optional, defaults to '/' }).listen(2022); ``` `createHTTPServer` accepts the [options every adapter takes](https://trpc.io/docs/server/adapters#common-options), like `onError` and `maxBatchSize`, plus `middleware`, `maxBodySize` and [`basePath`](https://trpc.io/docs/server/adapters/standalone#custom-basePath). ## Handling CORS & OPTIONS By default the standalone server will not respond to HTTP OPTIONS requests, or set any CORS headers. If you're not hosting in an environment which can handle this for you, like during local development, you may need to handle it. ### 1. Install cors You can add support yourself with the popular `cors` package ```bash yarn add cors yarn add -D @types/cors ``` For full information on how to configure this package, [check the docs](https://github.com/expressjs/cors#readme) ### 2. Configure the Standalone server This example just throws open CORS to any request, which is useful for development, but you can and should configure it more strictly in a production environment. ```ts no-check title="server.ts" import { createHTTPServer } from '@trpc/server/adapters/standalone'; import cors from 'cors'; import { appRouter } from './appRouter'; createHTTPServer({ middleware: cors(), router: appRouter, createContext() { return {}; }, }).listen(3333); ``` The `middleware` option will accept any function which resembles a connect/node.js middleware, so it can be used for more than `cors` handling if you wish. It is, however, intended to be a simple escape hatch and as such won't on its own allow you to compose multiple middlewares together. If you want to do this then you could: 1. Use an alternate adapter with more comprehensive middleware support, like the [Express adapter](https://trpc.io/docs/server/adapters/express) 2. Use a solution to compose middlewares such as [connect](https://github.com/senchalabs/connect) 3. Extend the Standalone `createHTTPHandler` with a custom http server (see below) ## Adding a handler to a Custom HTTP server `createHTTPServer` is returning an instance of Node's built-in [`http.Server`](https://nodejs.org/api/http.html#class-httpserver), which means that you have an access to all its properties and APIs. However, if `createHTTPServer` isn't enough for your use case, you can also use the standalone adapter's `createHTTPHandler` function to create your own HTTP server. For instance: ```ts no-check title="server.ts" import { createServer } from 'http'; import { createHTTPHandler } from '@trpc/server/adapters/standalone'; import { appRouter } from './appRouter'; const handler = createHTTPHandler({ router: appRouter, createContext() { return {}; }, }); createServer((req, res) => { /** * Handle the request however you like, * just call the tRPC handler when you're ready */ handler(req, res); }).listen(3001); ``` ## Custom base path for handling requests The Standalone adapter also supports a `basePath` option, which will slice the basePath from the beginning of the request path. ```ts no-check title="server.ts" import { createServer } from 'http'; import { createHTTPHandler } from '@trpc/server/adapters/standalone'; import { appRouter } from './appRouter'; const handler = createHTTPHandler({ router: appRouter, basePath: '/trpc/', }); createServer((req, res) => { if (req.url?.startsWith('/trpc/')) { return handler(req, res); } // [... insert your custom logic here ...] res.statusCode = 404; res.end('Not Found'); }).listen(3001); ``` ## HTTP2 The Standalone adapter also supports HTTP/2. ```ts no-check title="server.ts" import http2 from 'http2'; import { createHTTP2Handler } from '@trpc/server/adapters/standalone'; import { appRouter } from './_app'; import { createContext } from './context'; const handler = createHTTP2Handler({ router: appRouter, createContext, // basePath: '/trpc/', // optional, defaults to '/' }); const server = http2.createSecureServer( { key: '...', cert: '...', }, (req, res) => { /** * Handle the request however you like, * just call the tRPC handler when you're ready */ handler(req, res); }, ); server.listen(3001); ``` ```ts no-check title="context.ts" import { CreateHTTP2ContextOptions } from '@trpc/server/adapters/standalone'; export async function createContext(opts: CreateHTTP2ContextOptions) { opts.req; opts.res; opts.info; return {}; } export type Context = Awaited<ReturnType<typeof createContext>>; ``` ## Common mistakes - **Calling the server from another origin without CORS.** The browser's preflight `OPTIONS` request gets no CORS headers, so the browser blocks the call. Pass [`middleware: cors()`](https://trpc.io/docs/server/adapters/standalone#handling-cors--options), with the origins you allow. ## 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`