UNPKG

@trpc/next

Version:

The tRPC Next.js library

494 lines (377 loc) • 21.3 kB
--- 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 @trpc/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 (`@trpc/next`) is deprecated in favor of the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query) (`@trpc/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 @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@latest 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 '@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; ``` <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 '@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: () => ({}), }); ``` </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 '@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/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 `@trpc/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 '@trpc/next'; import { ssrPrepass } from '@trpc/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 (`@trpc/next`) is deprecated in favor of the [TanStack React Query integration](https://trpc.io/docs/client/tanstack-react-query) (`@trpc/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 '@trpc/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 '@trpc/client'; import { createServerSideHelpers } from '@trpc/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 '@trpc/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** | `@trpc/tanstack-react-query` | `@trpc/next` + `@trpc/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 `@trpc/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`