Next.js Adapter
On this page
Example app
| Description | Links |
|---|---|
| Next.js Minimal Starter |
Next.js example
Serving your tRPC router in a Next.js project is straight-forward. Just create an API handler in pages/api/trpc/[trpc].ts as shown below:
pages/api/trpc/[trpc].ts
import { function createNextApiHandler<TRouter extends AnyRouter>(opts: NodeHTTPHandlerOptions<TRouter, NextApiRequest, NextApiResponse>): NextApiHandlercreateNextApiHandler } from '@trpc/server/adapters/next';
import { function createContext(opts: CreateNextContextOptions): Promise<{}>createContext } from '../../../server/trpc/context';
import { const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from '../../../server/trpc/router/_app';
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,
createContext?: NodeHTTPCreateContextFn<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, NextApiRequest, NextApiResponse> | undefined
createContext,
});Handling CORS and other advanced usage
While you can usually just "set and forget" the API Handler as shown above, sometimes you might want to modify it further.
The API handler created by createNextApiHandler and equivalents in other frameworks is just a function that takes req and res objects. This means you can also modify those objects before passing them to the handler, for example to enable CORS.
pages/api/trpc/[trpc].ts
import type { NextApiRequest, type NextApiResponse<Data = any> = ServerResponse<IncomingMessage> & {
send: Send<Data>;
json: Send<Data>;
status: (statusCode: number) => NextApiResponse<Data>;
redirect(url: string): NextApiResponse<Data>;
redirect(status: number, url: string): NextApiResponse<Data>;
setDraftMode: (options: {
enable: boolean;
}) => NextApiResponse<Data>;
setPreviewData: (data: object | string, options?: {
maxAge?: number;
path?: string;
}) => NextApiResponse<Data>;
clearPreviewData: (options?: {
path?: string;
}) => NextApiResponse<Data>;
revalidate: (urlPath: string, opts?: {
unstable_onlyGenerated?: boolean;
}) => Promise<void>;
}
NextApiResponse } from 'next';
import { function createNextApiHandler<TRouter extends AnyRouter>(opts: NodeHTTPHandlerOptions<TRouter, NextApiRequest, NextApiResponse>): NextApiHandlercreateNextApiHandler } from '@trpc/server/adapters/next';
import { function createContext(opts: CreateNextContextOptions): Promise<{}>createContext } from '../../../server/trpc/context';
import { const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from '../../../server/trpc/router/_app';
// create the API handler, but don't return it yet
const const nextApiHandler: NextApiHandlernextApiHandler = 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,
createContext?: NodeHTTPCreateContextFn<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, NextApiRequest, NextApiResponse> | undefined
createContext,
});
// https://nextjs.org/docs/api-routes/introduction
export default async function function handler(req: NextApiRequest, res: NextApiResponse): Promise<unknown>handler(
req: NextApiRequestreq: NextApiRequest,
res: NextApiResponseres: type NextApiResponse<Data = any> = ServerResponse<IncomingMessage> & {
send: Send<Data>;
json: Send<Data>;
status: (statusCode: number) => NextApiResponse<Data>;
redirect(url: string): NextApiResponse<Data>;
redirect(status: number, url: string): NextApiResponse<Data>;
setDraftMode: (options: {
enable: boolean;
}) => NextApiResponse<Data>;
setPreviewData: (data: object | string, options?: {
maxAge?: number;
path?: string;
}) => NextApiResponse<Data>;
clearPreviewData: (options?: {
path?: string;
}) => NextApiResponse<Data>;
revalidate: (urlPath: string, opts?: {
unstable_onlyGenerated?: boolean;
}) => Promise<void>;
}
NextApiResponse,
) {
// We can use the response object to enable CORS
res: NextApiResponseres.OutgoingMessage<IncomingMessage>.setHeader(name: string, value: number | string | readonly string[]): NextApiResponsesetHeader('Access-Control-Allow-Origin', '*');
res: NextApiResponseres.OutgoingMessage<IncomingMessage>.setHeader(name: string, value: number | string | readonly string[]): NextApiResponsesetHeader('Access-Control-Request-Method', '*');
res: NextApiResponseres.OutgoingMessage<IncomingMessage>.setHeader(name: string, value: number | string | readonly string[]): NextApiResponsesetHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res: NextApiResponseres.OutgoingMessage<IncomingMessage>.setHeader(name: string, value: number | string | readonly string[]): NextApiResponsesetHeader('Access-Control-Allow-Headers', '*');
// If you need to make authenticated CORS calls then
// remove what is above and uncomment the below code
// Allow-Origin has to be set to the requesting domain that you want to send the credentials back to
// res.setHeader('Access-Control-Allow-Origin', 'http://example:6006');
// res.setHeader('Access-Control-Request-Method', '*');
// res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
// res.setHeader('Access-Control-Allow-Headers', 'content-type');
// res.setHeader('Referrer-Policy', 'no-referrer');
// res.setHeader('Access-Control-Allow-Credentials', 'true');
if (req: NextApiRequestreq.IncomingMessage.method?: string | undefinedmethod === 'OPTIONS') {
res: NextApiResponseres.ServerResponse<IncomingMessage>.writeHead(statusCode: number, statusMessage?: string, headers?: OutgoingHttpHeaders | OutgoingHttpHeader[]): NextApiResponse (+1 overload)writeHead(200);
return res: NextApiResponseres.Stream.Writable.end(cb?: () => void): NextApiResponse (+2 overloads)end();
}
// finally pass the request on to the tRPC handler
return const nextApiHandler: (req: NextApiRequest, res: NextApiResponse<any>) => unknown | Promise<unknown>nextApiHandler(req: NextApiRequestreq, res: NextApiResponseres);
}App Router (Route Handlers)
If you're using the Next.js App Router, use the fetch adapter instead, as App Router route handlers are based on the Web standard Request and Response objects. See the App Router setup guide for a complete walkthrough.
app/api/trpc/[trpc]/route.ts
import { function fetchRequestHandler<TRouter extends AnyRouter>(opts: FetchHandlerRequestOptions<TRouter>): Promise<Response>fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { function createTRPCContext(opts: FetchCreateContextFnOptions): {}createTRPCContext } from '../../trpc/init';
import { const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from '../../trpc/routers/_app';
const const handler: (req: Request) => Promise<Response>handler = (req: Requestreq: Request) =>
fetchRequestHandler<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: FetchHandlerRequestOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): Promise<Response>
fetchRequestHandler({
endpoint: stringendpoint: '/api/trpc',
req: Requestreq,
BaseHandlerOptions<BuiltRouter<{ ctx: object; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, Request>.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?: FetchCreateContextFn<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>> | undefined
createContext: function createTRPCContext(opts: FetchCreateContextFnOptions): {}createTRPCContext,
});
export { const handler: (req: Request) => Promise<Response>handler as const GET: (req: Request) => Promise<Response>
export GET
GET, const handler: (req: Request) => Promise<Response>handler as const POST: (req: Request) => Promise<Response>
export POST
POST };