Skills › Software Development › Backend & APIs
adapter-express
Mount tRPC as Express middleware with createExpressMiddleware() from @trpc/server/adapters/express. Access Express req/res in createContext via CreateExpressContextOptions. Mount at a path prefix like app.use('/trpc', …). Avoid global express.json() conflicting with tRPC body parsing for FormData.
Tools: express,cors
The full skill
—
name: adapter-express
description: >
Mount tRPC as Express middleware with createExpressMiddleware() from
@trpc/server/adapters/express. Access Express req/res in createContext via
CreateExpressContextOptions. Mount at a path prefix like app.use('/trpc', …).
Avoid global express.json() conflicting with tRPC body parsing for FormData.
type: core
library: trpc
library_version: '11.16.0'
requires:
– server-setup
sources:
– www/docs/server/adapters/express.md
– examples/express-server/src/server.ts
—
# tRPC — Adapter: Express
## Setup
“`ts
// server.ts
import { initTRPC } from '@trpc/server';
import * as trpcExpress from '@trpc/server/adapters/express';
import express from 'express';
import { z } from 'zod';
const createContext = ({
req,
res,
}: trpcExpress.CreateExpressContextOptions) => {
return { req, res };
};
type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
const appRouter = t.router({
greet: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => ({ greeting: `Hello, ${input.name}!` })),
});
export type AppRouter = typeof appRouter;
const app = express();
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
);
app.listen(4000, () => {
console.log('Listening on http://localhost:4000');
});
“`
## Core Patterns
### Accessing Express req/res in context
“`ts
import * as trpcExpress from '@trpc/server/adapters/express';
const createContext = ({
req,
res,
}: trpcExpress.CreateExpressContextOptions) => {
const token = req.headers.authorization?.split(' ')[1];
return { token, res };
};
type Context = Awaited<ReturnType<typeof createContext>>;
“`
`CreateExpressContextOptions` provides typed access to the Express `req` (IncomingMessage) and `res` (ServerResponse).
### Adding tRPC alongside existing Express routes
“`ts
import * as trpcExpress from '@trpc/server/adapters/express';
import cors from 'cors';
import express from 'express';
import { createContext } from './context';
import { appRouter } from './router';
const app = express();
app.use(cors());
app.get('/health', (_req, res) => {
res.json({ status: 'ok' });
});
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
);
app.listen(4000);
“`
### Limiting batch size with maxBatchSize
“`ts
import * as trpcExpress from '@trpc/server/adapters/express';
import express from 'express';
import { createContext } from './context';
import { appRouter } from './router';
const app = express();
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
maxBatchSize: 10,
}),
);
app.listen(4000);
“`
Requests batching more than `maxBatchSize` operations are rejected with a `400 Bad Request` error. Set `maxItems` on your client's `httpBatchLink` to the same value to avoid exceeding the limit.
## Common Mistakes
### HIGH Global express.json() consuming tRPC request body
Wrong:
“`ts
const app = express();
app.use(express.json()); // global body parser
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
);
“`
Correct:
“`ts
const app = express();
// Only apply body parser to non-tRPC routes
app.use('/api', express.json());
app.use(
'/trpc',
trpcExpress.createExpressMiddleware({
router: appRouter,
createContext,
}),
);
“`
If `express.json()` is applied globally before the tRPC middleware, it consumes and parses the request body. tRPC then receives an already-parsed body, which breaks FormData and binary content type handling.
Source: www/docs/server/non-json-content-types.md
## See Also
– **server-setup** — `initTRPC.create()`, router/procedure definition, context
– **adapter-standalone** — simpler adapter when Express middleware ecosystem is not needed
– **auth** — extracting JWT from `req.headers.authorization` in context
– Express docs: https://expressjs.com/en/guide/using-middleware.html