@trpc/next
Version:
137 lines (112 loc) • 4.18 kB
Markdown
# Static Site Generation
> Prefetch tRPC queries in getStaticProps with createTRPCOptionsProxy and dehydrate, and stop the client from refetching them
From https://trpc.io/docs/client/nextjs/pages-router/ssg
To build a page's data into static HTML, prefetch its queries in `getStaticProps`. It works like [prefetching in `getServerSideProps`](https://trpc.io/docs/client/nextjs/pages-router/ssr), except that it runs at build time, or when Next.js revalidates the page, so there's no request.
This page builds on the [Pages Router setup](https://trpc.io/docs/client/nextjs/pages-router/setup).
## Fetch data in `getStaticProps`
Make a query client and a tRPC proxy that calls your router directly, prefetch the queries, and return the cache as `trpcState`. There's no request to build the context from, so pass one that doesn't need it.
This example uses superjson, so the post's `createdAt` reaches the page as a `Date`. It needs the matching `_app` from [Using a data transformer](https://trpc.io/docs/client/nextjs/pages-router/setup#using-a-data-transformer).
```tsx no-check title="pages/posts/[id].tsx"
import { dehydrate, useQuery } from '/react-query';
import { createTRPCOptionsProxy } from '/tanstack-react-query';
import type {
GetStaticPaths,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next';
import { prisma } from '../../server/context';
import superjson from 'superjson';
import { appRouter } from '../../server/routers/_app';
import { makeQueryClient } from '../../utils/query-client';
import { useTRPC } from '../../utils/trpc';
export async function getStaticProps(
context: GetStaticPropsContext<{ id: string }>,
) {
const id = context.params?.id as string;
const queryClient = makeQueryClient();
const trpc = createTRPCOptionsProxy({
router: appRouter,
ctx: {},
queryClient,
});
await queryClient.prefetchQuery(trpc.post.byId.queryOptions({ id }));
return {
props: {
trpcState: superjson.serialize(dehydrate(queryClient)),
id,
},
revalidate: 1,
};
}
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await prisma.post.findMany({
select: {
id: true,
},
});
return {
paths: posts.map((post) => ({
params: {
id: post.id,
},
})),
// https://nextjs.org/docs/pages/api-reference/functions/get-static-paths#fallback-blocking
fallback: 'blocking',
};
};
export default function PostViewPage(
props: InferGetStaticPropsType<typeof getStaticProps>,
) {
const trpc = useTRPC();
const postQuery = useQuery(trpc.post.byId.queryOptions({ id: props.id }));
switch (postQuery.status) {
case 'pending':
return <p>Loading...</p>;
case 'error':
return <p>Error: {postQuery.error.message}</p>;
case 'success': {
const post = postQuery.data;
return (
<article>
<h1>{post.title}</h1>
<em>Created {post.createdAt.toLocaleDateString('en-us')}</em>
<p>{post.text}</p>
</article>
);
}
}
}
```
## Stop the client from refetching
TanStack React Query refetches stale data on the client when a component mounts, when the window regains focus, and when the network reconnects. If you only want the data from `getStaticProps`, for instance because your API is rate-limited, turn that off.
Per query:
```tsx no-check
import { useQuery } from '/react-query';
import { useTRPC } from './utils/trpc';
function Example() {
const trpc = useTRPC();
const exampleQuery = useQuery(
trpc.example.queryOptions(undefined, {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
}),
);
}
```
Or for every query, in the query client:
```ts no-check title="utils/query-client.ts"
import { QueryClient } from '/react-query';
export function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
refetchOnMount: false,
refetchOnWindowFocus: false,
refetchOnReconnect: false,
},
},
});
}
```
Be careful with the second one if your app mixes static and dynamic queries.