www/versioned_docs/version-10.x/client/nextjs/setup.mdx
import TabItem from '@theme/TabItem'; import Tabs from '@theme/Tabs';
We recommend a file structure like this one, although it is not enforced by tRPC. This is what you'll see in our examples. The rest of this page will take you through the process of adding tRPC in to this structure.
.
├── prisma # <-- if prisma is added
│ └── [..]
├── src
│ ├── pages
│ │ ├── _app.tsx # <-- add `withTRPC()`-HOC here
│ │ ├── api
│ │ │ └── trpc
│ │ │ └── [trpc].ts # <-- tRPC HTTP handler
│ │ └── [..]
│ ├── server
│ │ ├── routers
│ │ │ ├── _app.ts # <-- main app router
│ │ │ ├── post.ts # <-- sub routers
│ │ │ └── [..]
│ │ ├── context.ts # <-- create app context
│ │ └── trpc.ts # <-- procedure helpers
│ └── utils
│ └── trpc.ts # <-- your typesafe tRPC hooks
└── [..]
npm install @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
yarn add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
pnpm add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
bun add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
The Next.js integration is actually a combination of our React Query Integration and some Next.js specific integrations.
If you want to use Zod for input validation, make sure you have enabled strict mode in your tsconfig.json:
"compilerOptions": {
+ "strict": true
}
If strict mode is too harsh, you'll at least want to enable strictNullChecks:
"compilerOptions": {
+ "strictNullChecks": true
}
Initialize your tRPC backend in src/server/trpc.ts using the initTRPC function, and create your first router. We're going to make a simple "hello world" router and procedure here - but for deeper information on creating your tRPC API you should refer to:
import { initTRPC } from '@trpc/server';
// Avoid exporting the entire t-object
// since it's not very descriptive.
// For instance, the use of a t variable
// is common in i18n libraries.
const t = initTRPC.create();
// Base router and procedure helpers
export const router = t.router;
export const procedure = t.procedure;
import { z } from 'zod';
import { procedure, router } from '../trpc';
export const appRouter = router({
hello: procedure
.input(
z.object({
text: z.string(),
}),
)
.query((opts) => {
return {
greeting: `hello ${opts.input.text}`,
};
}),
});
// export type definition of API
export type AppRouter = typeof appRouter;
import * as trpcNext from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers/_app';
// export API handler
// @link https://trpc.io/docs/server/adapters
export default trpcNext.createNextApiHandler({
router: appRouter,
createContext: () => ({}),
});
:::note The backend above is using the recommended file structure, but you can keep it simple and put everything in an API handler directly if you prefer. :::
use the createTRPCNext function to create a set of strongly-typed hooks from your API's type signature.
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers/_app';
function getBaseUrl() {
if (typeof window !== 'undefined')
// browser should use relative path
return '';
if (process.env.VERCEL_URL)
// reference for vercel.com
return `https://${process.env.VERCEL_URL}`;
if (process.env.RENDER_INTERNAL_HOSTNAME)
// reference for render.com
return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;
// assume localhost
return `http://localhost:${process.env.PORT ?? 3000}`;
}
export const trpc = createTRPCNext<AppRouter>({
config(config) {
return {
links: [
httpBatchLink({
/**
* If you want to use SSR, you need to use the server's full URL
* @see https://trpc.io/docs/ssr
**/
url: `${getBaseUrl()}/api/trpc`,
// You can pass any HTTP headers you wish here
async headers() {
return {
// authorization: getAuthCookie(),
};
},
}),
],
};
},
/**
* @see https://trpc.io/docs/ssr
**/
ssr: false,
});
:::note
createTRPCNext does not work with the tRPC-v9 interop mode. If you are migrating from v9 using interop, you should continue using the old way of initializing tRPC.
:::
_app.tsxWrap your root app page in the trpc.withTRPC HOC, similar to this:
import type { AppType } from 'next/app';
import { trpc } from '../utils/trpc';
const MyApp: AppType = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);
You're all set!
You can now use the React hooks you have just created to invoke your API. For more detail see the React Query Integration
import { trpc } from '../utils/trpc';
export default function IndexPage() {
const hello = trpc.hello.useQuery({ text: 'client' });
if (!hello.data) {
return <div>Loading...</div>;
}
return (
<div>
<p>{hello.data.greeting}</p>
</div>
);
}
createTRPCNext() optionsconfig-callbackThe config-argument is a function that returns an object that configures the tRPC and React Query clients. This function has a ctx input that gives you access to the Next.js req object, among other things. The returned value can contain the following properties:
links to customize the flow of data between tRPC Client and the tRPC Server. Read more.queryClientConfig: a configuration object for the React Query QueryClient used internally by the tRPC React hooks: QueryClient docsqueryClient: a React Query QueryClient instance
queryClient or a queryClientConfig.transformer: a transformer applied to outgoing payloads. Read more about Data TransformersabortOnUnmount: determines if in-flight requests will be cancelled on component unmount. This defaults to false.overrides: (default: undefined) {#overrides}Configure overrides for React Query's hooks.
ssr-boolean (default: false)Whether tRPC should await queries when server-side rendering a page. Defaults to false.
responseMeta-callbackAbility to set request headers and HTTP status when server-side rendering.
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../pages/api/trpc/[trpc]';
export const trpc = createTRPCNext<AppRouter>({
config(config) {
/* [...] */
},
ssr: true,
responseMeta(opts) {
const { clientErrors } = opts;
if (clientErrors.length) {
// propagate first http error from API calls
return {
status: clientErrors[0].data?.httpStatus ?? 500,
};
}
// cache full page for 1 day + revalidate once every second
const ONE_DAY_IN_SECONDS = 60 * 60 * 24;
return {
'Cache-Control': `s-maxage=1, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,
};
},
});
Browse the rest of the docs to learn more about things like authorization, middlewares, and error handling.
You can also find information about queries and mutations now that you're using @trpc/react-query.