@trpc/next
Version:
494 lines (377 loc) • 21.3 kB
Markdown
---
name: nextjs-pages-router
description: Set up tRPC in Next.js Pages Router with createNextApiHandler, createTRPCNext (config, transformer, ssr, ssrPrepass, responseMeta) and the withTRPC HOC over /react-query hooks. Prefetch selected pages with createServerSideHelpers in getServerSideProps or getStaticProps and return trpcState, or SSR every query with ssr true, forwarding request headers.
requires:
- server-setup
- client-setup
sources:
- www/v11/docs/client/nextjs/pages-router/setup.md
- www/v11/docs/client/nextjs/pages-router/server-side-helpers.md
- www/v11/docs/client/nextjs.md
- www/v11/docs/client/nextjs/pages-router/ssr.md
- www/v11/docs/client/nextjs/pages-router/ssg.md
- www/v11/docs/server/adapters/nextjs.md
- www/v11/docs/client/nextjs/pages-router/aborting-procedure-calls.md
- www/v11/docs/client/react/useQuery.md
- www/v11/docs/client/react/useMutation.md
- www/v11/docs/client/react/useUtils.md
metadata:
type: framework
library: trpc
library_version: 11.23.0
---
<!-- Generated from www/v11/docs/client/nextjs/pages-router/setup.md by www/build/skills.ts. Edit the docs instead. -->
# Set up with Next.js Pages Router (deprecated)
From https://trpc.io/docs/client/nextjs/pages-router/setup
> [!WARNING] Deprecated
> The Next.js Pages Router integration (`/next`) is deprecated in favor of the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query) (`/tanstack-react-query`). To use it with Next.js, follow [Set up with Next.js App Router](https://trpc.io/docs/client/nextjs/app-router-setup).
> [!WARNING] Caution
> This guide is for the **Next.js Pages Router**. If you are using the **Next.js App Router**, see the [App Router setup guide](https://trpc.io/docs/client/nextjs/app-router-setup) instead.
## Recommended file structure
We recommend a file structure like this one, although it is not enforced by tRPC. This is what you'll see in [our examples](https://trpc.io/docs/example-apps). The rest of this page will take you through the process of adding tRPC in to this structure.
```graphql
.
├── 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
└── [..]
```
## Add tRPC to existing Next.js project
### 1. Install deps
```bash
npm install /server @trpc/client /react-query @trpc/next /react-query zod
```
The Next.js integration is actually a combination of our [classic React Query Integration](https://trpc.io/docs/client/react) (deprecated: it only gets bug fixes, and will be removed in the next major version of tRPC in favor of the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query)) and some Next.js-specific integrations.
### 2. Enable strict mode
If you want to use Zod for input validation, make sure you have enabled strict mode in your `tsconfig.json`:
```diff title="tsconfig.json"
"compilerOptions": {
+ "strict": true
}
```
If strict mode is too harsh, you'll at least want to enable `strictNullChecks`:
```diff title="tsconfig.json"
"compilerOptions": {
+ "strictNullChecks": true
}
```
### 3. Create a tRPC router
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:
- the [Quickstart guide](https://trpc.io/docs/quickstart) and [Backend usage docs](https://trpc.io/docs/server/overview) for tRPC information
- the [Next.js Adapter docs](https://trpc.io/docs/server/adapters/nextjs) for mounting tRPC within your Next.js server.
<details>
<summary>View sample backend</summary>
```ts no-check title="server/trpc.ts"
import { initTRPC } from '/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;
```
<br />
```ts no-check title="server/routers/_app.ts"
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;
```
<br />
```ts no-check title="pages/api/trpc/[trpc].ts"
import * as trpcNext from '/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: () => ({}),
});
```
</details>
> [!NOTE]
> The backend above is using the [recommended file structure](https://trpc.io/docs/client/nextjs/pages-router/setup#recommended-file-structure), but you can keep it simple and put everything in [an API handler directly](https://github.com/trpc/trpc/blob/main/examples/next-minimal-starter/src/pages/api/trpc/%5Btrpc%5D.ts) if you prefer.
### 4. Create tRPC hooks
Use the `createTRPCNext` function to create a set of strongly-typed hooks from your API's type signature.
```tsx no-check title="utils/trpc.ts"
import { httpBatchLink } from '/client';
import { createTRPCNext } from '/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/client/nextjs/pages-router/ssr
**/
url: `${getBaseUrl()}/api/trpc`,
// You can pass any HTTP headers you wish here
async headers() {
return {
// authorization: getAuthCookie(),
};
},
}),
],
};
},
/**
* @see https://trpc.io/docs/client/nextjs/pages-router/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](https://trpc.io/docs/v9/nextjs#4-create-trpc-hooks).
### 5. Configure `_app.tsx`
Wrap your root app page in the `trpc.withTRPC` HOC, similar to this:
```tsx no-check title="pages/_app.tsx"
import type { AppType } from 'next/app';
import { trpc } from '../utils/trpc';
const MyApp: AppType = ({ Component, pageProps }) => {
return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);
```
### 6. Make an API request
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](https://trpc.io/docs/client/react/setup)
```tsx no-check title="pages/index.tsx"
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()` options
### `config`-callback
The `config`-argument is a function that returns an object that configures the tRPC and React Query clients. This function receives an object with an optional `ctx` property (of type `NextPageContext`) that gives you access to the Next.js `req` object during server-side rendering. The returned value can contain the following properties:
- **Required**:
- `links` to customize the flow of data between tRPC Client and the tRPC Server. [Read more](https://trpc.io/docs/client/links).
- Optional:
- `queryClientConfig`: a configuration object for the React Query `QueryClient` used internally by the tRPC React hooks: [QueryClient docs](https://tanstack.com/query/v5/docs/reference/QueryClient)
- `queryClient`: a React Query [QueryClient instance](https://tanstack.com/query/v5/docs/reference/QueryClient)
- **Note:** You can only provide either a `queryClient` or a `queryClientConfig`.
- `abortOnUnmount`: determines if in-flight requests will be cancelled on component unmount. This defaults to `false`.
### `transformer`
If your router uses a [data transformer](https://trpc.io/docs/server/data-transformers), pass it here as well as to your links. tRPC uses it to read the `trpcState` that [SSR](https://trpc.io/docs/client/nextjs/pages-router/ssr), [SSG](https://trpc.io/docs/client/nextjs/pages-router/ssg) and the [server-side helpers](https://trpc.io/docs/client/nextjs/pages-router/server-side-helpers) send to the page.
### `overrides`: (default: `undefined`)
Configure [overrides for React Query's hooks](https://trpc.io/docs/client/react/useUtils#invalidate-full-cache-on-every-mutation).
### `ssr`-boolean (default: `false`)
Whether tRPC should await queries when server-side rendering a page. Defaults to `false`. Enabling it requires the `ssrPrepass` option, imported from `/next/ssrPrepass`, and you can pass a function of the request instead of `true`. See [SSR](https://trpc.io/docs/client/nextjs/pages-router/ssr).
### `responseMeta`-callback
Ability to set response headers and HTTP status when server-side rendering, so it requires `ssr`. It receives the request's `ctx` and the `clientErrors` of the queries that failed. Return `headers` as a plain object: a `Headers` instance is ignored here.
#### Example
```tsx no-check title="utils/trpc.ts"
import { createTRPCNext } from '/next';
import { ssrPrepass } from '/next/ssrPrepass';
import type { AppRouter } from '../server/routers/_app';
export const trpc = createTRPCNext<AppRouter>({
config(config) {
return {
links: [
/* [...] */
],
};
},
ssr: true,
ssrPrepass,
responseMeta(opts) {
const error = opts.clientErrors[0];
if (error) {
// propagate the HTTP status of the first failed query
return { status: error.data?.httpStatus ?? 500 };
}
return {
headers: { 'cache-control': 's-maxage=1, stale-while-revalidate=86400' },
};
},
});
```
## Common mistakes
- **Turning on `ssr: true` without weighing the cost.** It adds a `getInitialProps` to your app, which turns off Next.js' Automatic Static Optimization and [doesn't mix well with `getServerSideProps`](https://trpc.io/docs/client/nextjs/pages-router/ssr). It also renders the page repeatedly until no query is fetching, so every query delays the response. To prefetch only some pages, keep `ssr: false` and use the [server-side helpers](https://trpc.io/docs/client/nextjs/pages-router/server-side-helpers) in `getServerSideProps` or `getStaticProps`.
- **Not forwarding the request's headers during SSR.** The server-side render calls your API without the user's cookies unless the link's `headers()` passes on `ctx.req.headers`, and it needs an absolute URL. See [SSR](https://trpc.io/docs/client/nextjs/pages-router/ssr).
- **Using Suspense with `ssr: true`.** If a query fails while rendering on the server, the whole page fails, even inside an `<ErrorBoundary />`.
- **Returning the dehydrated state under another key.** `withTRPC` only reads `pageProps.trpcState`, so return `{ props: { trpcState: helpers.dehydrate() } }`. Otherwise the client fetches every query again.
- **Mixing in App Router setup.** `createTRPCNext` and `withTRPC` are for the Pages Router. In the App Router, follow the [App Router setup](https://trpc.io/docs/client/nextjs/app-router-setup) instead.
## Next steps
Browse the rest of the docs to learn more about things like [authorization](https://trpc.io/docs/server/authorization), [middlewares](https://trpc.io/docs/server/middlewares), and [error handling](https://trpc.io/docs/server/error-handling).
You can also find information about [queries](https://trpc.io/docs/client/react/useQuery) and [mutations](https://trpc.io/docs/client/react/useMutation) now that you're using `@trpc/react-query`.
## Server-Side Helpers
From https://trpc.io/docs/client/nextjs/pages-router/server-side-helpers
> [!WARNING] Deprecated
> The Next.js Pages Router integration (`/next`) is deprecated in favor of the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query) (`/tanstack-react-query`). To use it with Next.js, follow [Set up with Next.js App Router](https://trpc.io/docs/client/nextjs/app-router-setup).
The server-side helpers provide you with a set of helper functions that you can use to prefetch queries on the server. This is useful for SSG, but also for SSR if you opt not to use `ssr: true`.
Prefetching via the server-side helpers allows populating the query cache on the server, which means that these queries do not have to fetch on the client initially.
### There are 2 ways to use the server-side helpers.
#### 1. Internal router
This method is used when you have direct access to your tRPC router, e.g., when developing a monolithic Next.js application.
Using the helpers makes tRPC call your procedures directly on the server, without an HTTP request, similar to [server-side calls](https://trpc.io/docs/server/server-side-calls).
That also means that you don't have the request and response at hand like you usually do. Make sure you're instantiating the server-side helpers with a context without `req` & `res`, which are typically filled via the context creation. We recommend the concept of ["inner" and "outer" context](https://trpc.io/docs/server/context) in that scenario.
```ts no-check
import { createServerSideHelpers } from '/react-query/server';
import { createContext } from './server/context';
import { appRouter } from './server/routers/_app';
import superjson from 'superjson';
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createContext(),
transformer: superjson,
});
```
#### 2. External router
This method is used when you don't have direct access to your tRPC router, e.g., when developing a Next.js application and a standalone API hosted separately.
```ts no-check
import { createTRPCClient, httpBatchLink } from '/client';
import { createServerSideHelpers } from '/react-query/server';
import type { AppRouter } from './server/router';
import superjson from 'superjson';
const proxyClient = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: 'http://localhost:3000/api/trpc',
}),
],
});
const helpers = createServerSideHelpers({
client: proxyClient,
});
```
### Helpers usage
The server-side helpers methods return an object that mirrors your router structure, with all of your routers as keys. However, rather than `useQuery` and `useMutation`, you get `prefetch`, `fetch`, `prefetchInfinite`, and `fetchInfinite` functions.
The primary difference between `prefetch` and `fetch` is that `fetch` acts much like a normal function call, returning the result of the query, whereas `prefetch` does not return the result and never throws - if you need that behavior, use `fetch` instead. Instead, `prefetch` will add the query to the cache, which you then dehydrate and send to the client.
```ts no-check
// In getServerSideProps / getStaticProps:
const props = {
// very important - use `trpcState` as the key
trpcState: helpers.dehydrate(),
};
```
The rule of thumb is `prefetch` for queries that you know you'll need on the client, and `fetch` for queries that you want to use the result of on the server.
The functions are all wrappers around react-query functions. Please check out [their docs](https://tanstack.com/query/v5/docs/framework/react/overview) to learn more about them in detail.
> [!NOTE] Info
> For a full example, see our [E2E SSG test example](https://github.com/trpc/trpc/tree/main/examples/.test/ssg)
### Next.js Example
```tsx no-check title="pages/posts/[id].tsx"
import { createServerSideHelpers } from '/react-query/server';
import { appRouter } from './server/routers/_app';
import { trpc } from './utils/trpc';
import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';
import superjson from 'superjson';
export async function getServerSideProps(
context: GetServerSidePropsContext<{ id: string }>,
) {
const helpers = createServerSideHelpers({
router: appRouter,
ctx: {},
transformer: superjson,
});
const id = context.params?.id as string;
/*
* Prefetching the `post.byId` query.
* `prefetch` does not return the result and never throws - if you need that behavior, use `fetch` instead.
*/
await helpers.post.byId.prefetch({ id });
// Make sure to return { props: { trpcState: helpers.dehydrate() } }
return {
props: {
trpcState: helpers.dehydrate(),
id,
},
};
}
export default function PostViewPage(
props: InferGetServerSidePropsType<typeof getServerSideProps>,
) {
const { id } = props;
const postQuery = trpc.post.byId.useQuery({ id });
if (postQuery.status !== 'success') {
// won't happen since the query has been prefetched
return <>Loading...</>;
}
const { data } = postQuery;
return (
<>
<h1>{data.title}</h1>
<em>Created {data.createdAt.toLocaleDateString()}</em>
<p>{data.text}</p>
<h2>Raw data:</h2>
<pre>{JSON.stringify(data, null, 4)}</pre>
</>
);
}
```
## Choosing between App Router and Pages Router
| | App Router | Pages Router |
| ------------------- | --------------------------------------------- | --------------------------------------------------- |
| **Recommended for** | New projects | Existing Pages Router projects |
| **Data fetching** | Server Components, `prefetchQuery` | `getServerSideProps`, `getStaticProps`, SSR via HOC |
| **Server adapter** | [Fetch adapter](https://trpc.io/docs/server/adapters/fetch) | [Next.js adapter](https://trpc.io/docs/server/adapters/nextjs) |
| **Client package** | `/tanstack-react-query` | `@trpc/next` + `/react-query` |
| **Provider setup** | Manual `QueryClientProvider` + `TRPCProvider` | Automatic via `withTRPC()` HOC |
> [!TIP]
> If you're starting a new project, use the App Router setup with `/tanstack-react-query`. The Pages Router integration (`@trpc/next`) is deprecated in favor of the TanStack React Query integration.
From https://trpc.io/docs/client/nextjs#choosing-between-app-router-and-pages-router
## References
- [Server-Side Rendering](references/client-nextjs-pages-router-ssr.md): Render every tRPC query on the server with ssr: true and ssrPrepass, forward request headers, and cache SSR responses with responseMeta
- [Static Site Generation](references/client-nextjs-pages-router-ssg.md): Prefetch tRPC queries in getStaticProps with the server-side helpers and stop the client from refetching them
- [Next.js Adapter](references/server-adapters-nextjs.md): Serve tRPC from a Next.js API route with createNextApiHandler, or from an App Router route handler with fetchRequestHandler
- [Aborting Procedure Calls](references/client-nextjs-pages-router-aborting-procedure-calls.md): Cancel in-flight requests when a component unmounts with abortOnUnmount, globally or per request
- [useQuery()](references/client-react-useQuery.md): Fetch data with the classic trpc.procedure.useQuery() hook, its tRPC-specific options and streaming
- [useMutation()](references/client-react-useMutation.md): Call mutations with the classic trpc.procedure.useMutation() hook
- [useUtils](references/client-react-useUtils.md): Invalidate, refetch, prefetch and update cached query data with the classic trpc.useUtils() helpers
## Related skills
- `server-setup` (https://trpc.io/docs/server/overview): `npx @tanstack/intent@latest load @trpc/server#server-setup`
- `client-setup` (https://trpc.io/docs/client/vanilla/setup): `npx @tanstack/intent@latest load @trpc/client#client-setup`