HTTP Batch Link
On this page
httpBatchLink is a terminating link that batches an array of individual tRPC operations into a single HTTP request that's sent to a single tRPC procedure.
Usage
You can import and add the httpBatchLink to the links array as such:
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpBatchLink<TRouter extends AnyRouter>(opts: HTTPBatchLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpBatchLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from './server';
const const client: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
client = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpBatchLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPBatchLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
httpBatchLink({
url: string | URLurl: 'http://localhost:3000',
// transformer,
}),
],
});After that, you can make use of batching by setting all your procedures in a Promise.all. The code below will produce exactly one HTTP request and on the server exactly one database query:
const const somePosts: [{
id: number;
title: string;
}, {
id: number;
title: string;
}, {
id: number;
title: string;
}]
somePosts = await var Promise: PromiseConstructorPromise.PromiseConstructor.all<[TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>, TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>, TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>]>(values: [TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>, TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>, TRPCTaggedPromise<...>]): Promise<...> (+1 overload)
all([
const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc.post: DecoratedProcedureRecord<{
transformer: false;
errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.byId: {
query: Resolver<{
input: number;
output: {
id: number;
title: string;
};
errorShape: DefaultErrorShape;
transformer: false;
}>;
}
byId.query: (input: number, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>
query(1),
const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc.post: DecoratedProcedureRecord<{
transformer: false;
errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.byId: {
query: Resolver<{
input: number;
output: {
id: number;
title: string;
};
errorShape: DefaultErrorShape;
transformer: false;
}>;
}
byId.query: (input: number, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>
query(2),
const trpc: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
post: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>>>
trpc.post: DecoratedProcedureRecord<{
transformer: false;
errorShape: DefaultErrorShape;
}, DecorateCreateRouterOptions<{
byId: QueryProcedure<{
input: number;
output: {
id: number;
title: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
post.byId: {
query: Resolver<{
input: number;
output: {
id: number;
title: string;
};
errorShape: DefaultErrorShape;
transformer: false;
}>;
}
byId.query: (input: number, opts?: TRPCProcedureOptions) => TRPCTaggedPromise<{
id: number;
title: string;
}, DefaultErrorShape>
query(3),
]);httpBatchLink Options
The httpBatchLink function takes an options object that has the HTTPBatchLinkOptions shape.
export interface HTTPBatchLinkOptions extends HTTPLinkOptions {
/**
* Maximum length of HTTP URL allowed before operations are split into multiple requests
* @default Infinity
*/
HTTPBatchLinkOptions.maxURLLength?: number | undefinedmaxURLLength?: number;
/**
* Maximum number of operations allowed in a single batch request
* @default Infinity
*/
HTTPBatchLinkOptions.maxItems?: number | undefinedmaxItems?: number;
}
export interface HTTPLinkOptions {
HTTPLinkOptions.url: string | URLurl: string | URL;
/**
* Add ponyfill for fetch
*/
HTTPLinkOptions.fetch?: ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) | undefinedfetch?: typeof function fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>fetch;
/**
* Data transformer
* @see https://trpc.io/docs/server/data-transformers
**/
HTTPLinkOptions.transformer?: anytransformer?: type DataTransformerOptions = anyDataTransformerOptions;
/**
* Headers to be set on outgoing requests or a callback that of said headers
* @see https://trpc.io/docs/client/headers
*/
HTTPLinkOptions.headers?: HTTPHeaders | ((opts: {
opList: NonEmptyArray<Operation>;
}) => HTTPHeaders | Promise<HTTPHeaders>) | undefined
headers?:
| type HTTPHeaders = {
[x: string]: string | string[];
}
HTTPHeaders
| ((opts: {
opList: NonEmptyArray<Operation>;
}
opts: { opList: NonEmptyArray<Operation>opList: type NonEmptyArray<T> = [T, ...T[]]NonEmptyArray<type Operation = {
id: number;
type: "query" | "mutation" | "subscription";
path: string;
input: unknown;
}
Operation> }) => type HTTPHeaders = {
[x: string]: string | string[];
}
HTTPHeaders | interface Promise<T>Promise<type HTTPHeaders = {
[x: string]: string | string[];
}
HTTPHeaders>);
}Setting a maximum URL length
When sending batch requests, sometimes the URL can become too large causing HTTP errors like 413 Payload Too Large, 414 URI Too Long, and 404 Not Found. The maxURLLength option will limit the number of requests that can be sent together in a batch.
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpBatchLink<TRouter extends AnyRouter>(opts: HTTPBatchLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpBatchLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from './server';
const const client: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
client = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpBatchLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPBatchLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
httpBatchLink({
url: string | URLurl: 'http://localhost:3000',
maxURLLength?: number | undefinedmaxURLLength: 2083, // a suitable size
// alternatively, you can make all RPC-calls to be called with POST
// methodOverride: 'POST',
}),
],
});Limiting batch size
1. Set a maximum batch size on your server:
maxBatchSize limits how many operations may be sent in a single batch request. Requests exceeding this limit will be rejected with a 400 Bad Request error. This can be passed to any tRPC adapter.
import { createHTTPServer } from '@trpc/server/adapters/standalone';
createHTTPServer({
maxBatchSize: 10,
});or for example if using Next.js:
export default trpcNext.createNextApiHandler({
maxBatchSize: 10,
});2. Set a matching limit on the client:
Use the maxItems option on your batch link to ensure the client doesn't exceed the server's limit. This splits large batches into multiple HTTP requests automatically
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '../server';
const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000',
// 👇 should be the same or lower than the server's maxBatchSize
maxItems: 10,
}),
],
});Disabling request batching
1. Disable batching on your server:
import { function createHTTPServer<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): http.Server<any, any>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<{}>>>): http.Server<any, any>
createHTTPServer({
BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, http.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,
// 👇 disable batching
BaseHandlerOptions<TRouter extends AnyRouter, TRequest>.allowBatching?: boolean | undefinedallowBatching: false,
});or, if you're using Next.js:
import { function createNextApiHandler<TRouter extends AnyRouter>(opts: NodeHTTPHandlerOptions<TRouter, NextApiRequest, NextApiResponse>): NextApiHandlercreateNextApiHandler } from '@trpc/server/adapters/next';
import { const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from '../../../router';
export default createNextApiHandler<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: NodeHTTPHandlerOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, NextApiRequest, NextApiResponse>): NextApiHandler
createNextApiHandler({
BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, NextApiRequest>.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,
// 👇 disable batching
BaseHandlerOptions<TRouter extends AnyRouter, TRequest>.allowBatching?: boolean | undefinedallowBatching: false,
});2. Replace httpBatchLink with httpLink in your tRPC Client
import { function createTRPCClient<TRouter extends AnyRouter>(opts: CreateTRPCClientOptions<TRouter>): TRPCClient<TRouter>createTRPCClient, function httpLink<TRouter extends AnyRouter = AnyRouter>(opts: HTTPLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpLink } from '@trpc/client';
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from './server';
const const client: TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
client = createTRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateTRPCClientOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): TRPCClient<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>
createTRPCClient<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
httpLink({
url: string | URLurl: 'http://localhost:3000',
}),
],
});or, if you're using Next.js:
import type { type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter } from '../server';
import { function httpLink<TRouter extends AnyRouter = AnyRouter>(opts: HTTPLinkOptions<TRouter["_def"]["_config"]["$types"]>): TRPCLink<TRouter>httpLink } from '@trpc/client';
import { function createTRPCNext<TRouter extends AnyRouter, TSSRContext extends NextPageContext = NextPageContext>(opts: WithTRPCNoSSROptions<TRouter> | WithTRPCSSROptions<TRouter>): CreateTRPCNext<TRouter, TSSRContext>createTRPCNext } from '@trpc/next';
export const const trpc: CreateTRPCNextBase<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, NextPageContext> & DecorateRouterRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
trpc = createTRPCNext<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, NextPageContext>(opts: WithTRPCNoSSROptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>> | WithTRPCSSROptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<...>>>): CreateTRPCNextBase<...> & DecorateRouterRecord<...>
createTRPCNext<type AppRouter = Router<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>> & DecorateCreateRouterOptions<{}>
AppRouter>({
config: (info: {
ctx?: NextPageContext;
}) => WithTRPCConfig<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
config() {
return {
links: TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>[]
links: [
httpLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: HTTPLinkOptions<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>): TRPCLink<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>
httpLink({
url: string | URLurl: '/api/trpc',
}),
],
};
},
});