Skip to content
tRPC

Partners

Overview

On this page

tRPC is not a server on its own, and must therefore be served using other hosts, such as a simple Node.js HTTP Server, Express, or even Next.js. Most tRPC features are the same no matter which backend you choose. Adapters act as the glue between the host system and your tRPC API.

Adapters typically follow some common conventions, allowing you to set up context creation via createContext, and globally handle errors via onError, but importantly allow you to choose an appropriate host for your application.

We support many modes of hosting an API, which you will find documented here.

  • For serverful APIs, you might want our Standalone adapter, or use the Express or Fastify adapters to hook into your existing APIs
  • You might want a serverless solution and choose AWS Lambda, or Fetch for edge runtimes
  • You might have a full-stack framework and want a full integration like Next.js, or you could use the Fetch adapter with Next.js, Astro, Remix, or SolidStart

Common options

Every HTTP adapter accepts these options, next to its own (like endpoint for Fetch, or basePath for Standalone):

OptionDescription
routerYour app router. Required.
createContextCreates the context for each request. It receives the adapter's own objects (e.g. req and res) plus info.
onErrorCalled with { error, type, path, input, ctx, req } for every error, e.g. to report it. See Error handling.
responseMetaReturns { status, headers } for the response, e.g. for caching.
allowBatchingSet to false to reject batch requests. Defaults to true.
maxBatchSizeRejects 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.
allowMethodOverrideAccepts 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.
  • middleware: a connect-style (req, res, next) function that runs before tRPC, e.g. cors().
server.ts
import { function createHTTPServer<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): Server<typeof IncomingMessage, typeof ServerResponse>createHTTPServer } from '@trpc/server/adapters/standalone';
import { 
const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter
} from './router';
createHTTPServer<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateHTTPHandlerOptions<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>>): Server<typeof IncomingMessage, typeof ServerResponse>
createHTTPServer
({
BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, IncomingMessage>.router: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>
router
:
const appRouter: BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter
,
createContext?: NodeHTTPCreateContextFn<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>, IncomingMessage, ServerResponse<IncomingMessage>> | undefined
createContext
: () => ({}),
BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, IncomingMessage>.onError?: HTTPErrorHandler<BuiltRouter<{
    ctx: object;
    meta: object;
    errorShape: DefaultErrorShape;
    transformer: false;
}, DecorateCreateRouterOptions<{}>>, IncomingMessage> | undefined
onError
({ error: TRPCErrorerror, path: string | undefinedpath }) {
var console: Consoleconsole.Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)error(`tRPC error on '${path: string | undefinedpath}':`, error: TRPCErrorerror); }, BaseHandlerOptions<TRouter extends AnyRouter, TRequest>.maxBatchSize?: number | undefinedmaxBatchSize: 10, maxBodySize?: number | undefinedmaxBodySize: 1024 * 1024, // 1 MiB }).Server.listen(port?: number, hostname?: string, backlog?: number, listeningListener?: () => void): Server<typeof IncomingMessage, typeof ServerResponse> (+8 overloads)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.

Pages under Adapters