www/docs/server/adapters/standalone.md
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 with the normal options related to tRPC.
If you have an existing API deployment like Express, Fastify, or Next.js, 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 and 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.
:::tip AI Agents If you use an AI coding agent, install tRPC skills for better code generation:
npx @tanstack/intent@latest install
:::
Implement your tRPC router. For example:
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
The Standalone adapter runs a simple Node.js HTTP server.
// @filename: appRouter.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});
// @filename: server.ts
// ---cut---
import { createHTTPServer } from '@trpc/server/adapters/standalone';
import { appRouter } from './appRouter';
createHTTPServer({
router: appRouter,
createContext() {
return {};
},
// basePath: '/trpc/', // optional, defaults to '/'
}).listen(2022);
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.
You can add support yourself with the popular cors package
yarn add cors
yarn add -D @types/cors
For full information on how to configure this package, check the docs
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.
// @filename: appRouter.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});
// @filename: server.ts
// ---cut---
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:
createHTTPHandler with a custom http server (see below)createHTTPServer is returning an instance of Node's built-in http.Server, 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:
// @types: node
// @filename: appRouter.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});
// @filename: server.ts
// ---cut---
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);
The Standalone adapter also supports a basePath option, which will slice the basePath from the beginning of the request path.
// @types: node
// @filename: appRouter.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});
// @filename: server.ts
// ---cut---
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);
The Standalone adapter also supports HTTP/2.
// @types: node
// @filename: _app.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});
// @filename: context.ts
import type { CreateHTTP2ContextOptions } from '@trpc/server/adapters/standalone';
export async function createContext(opts: CreateHTTP2ContextOptions) {
return {};
}
// @filename: server.ts
// ---cut---
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);
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>>;