サーバーサイドヘルパー
サーバーサイドヘルパーは、サーバー上でクエリをプリフェッチするために使用できる一連のヘルパー関数を提供します。これはSSGに役立ちますが、ssr: true
を使用しない場合はSSRにも役立ちます。
サーバーサイドヘルパーを介したプリフェッチにより、サーバー上のクエリキャッシュにデータを入力できます。つまり、これらのクエリは最初にクライアントでフェッチする必要がありません。
サーバーサイドヘルパーを使用するには2つの方法があります。
1. 内部ルーター
この方法は、tRPCルーターに直接アクセスできる場合に使用します。例:モノリシックなNext.jsアプリケーションを開発する場合。
ヘルパーを使用すると、tRPCはサーバーサイドコールと同様に、HTTPリクエストなしでサーバー上でプロシージャを直接呼び出します。これは、通常のようにリクエストとレスポンスを手元に置いていないことも意味します。通常はコンテキストの作成によって設定される req
と res
を含まないコンテキストを使用して、サーバーサイドヘルパーをインスタンス化していることを確認してください。そのシナリオでは、"内部" コンテキストと "外部" コンテキストの概念をお勧めします。
ts
import { createServerSideHelpers } from '@trpc/react-query/server';import { createContext } from '~/server/context';import superjson from 'superjson';const helpers = createServerSideHelpers({router: appRouter,ctx: await createContext(),transformer: superjson, // optional - adds superjson serialization});
ts
import { createServerSideHelpers } from '@trpc/react-query/server';import { createContext } from '~/server/context';import superjson from 'superjson';const helpers = createServerSideHelpers({router: appRouter,ctx: await createContext(),transformer: superjson, // optional - adds superjson serialization});
2. 外部ルーター
この方法は、tRPCルーターに直接アクセスできない場合に使用します。例:Next.jsアプリケーションと個別にホストされているスタンドアロンAPIを開発する場合。
ts
import { createTRPCClient } from '@trpc/client';import { createServerSideHelpers } from '@trpc/react-query/server';import superjson from 'superjson';const proxyClient = createTRPCClient<AppRouter>({links: [httpBatchLink({url: 'http://localhost:3000/api/trpc',}),],});const helpers = createServerSideHelpers({client: proxyClient,});
ts
import { createTRPCClient } from '@trpc/client';import { createServerSideHelpers } from '@trpc/react-query/server';import superjson from 'superjson';const proxyClient = createTRPCClient<AppRouter>({links: [httpBatchLink({url: 'http://localhost:3000/api/trpc',}),],});const helpers = createServerSideHelpers({client: proxyClient,});
ヘルパーの使い方
サーバーサイドヘルパーメソッドは、すべてのルーターをキーとして持つ、tRPCクライアントによく似たオブジェクトを返します。ただし、useQuery
と useMutation
ではなく、prefetch
、fetch
、prefetchInfinite
、および fetchInfinite
関数が提供されます。
prefetch
と fetch
の主な違いは、fetch
は通常の関数呼び出しのように動作し、クエリの結果を返すのに対し、prefetch
は結果を返さず、スローしません。その動作が必要な場合は、代わりに fetch
を使用してください。代わりに、prefetch
はクエリをキャッシュに追加し、それをデハイドレートしてクライアントに送信します。
ts
return {props: {// very important - use `trpcState` as the keytrpcState: helpers.dehydrate(),},};
ts
return {props: {// very important - use `trpcState` as the keytrpcState: helpers.dehydrate(),},};
経験則として、クライアントで必要なクエリには prefetch
を、サーバーで結果を使用するクエリには fetch
を使用します。
関数はすべてreact-query関数のラッパーです。詳細については、react-queryのドキュメントをご覧ください。
完全な例については、E2E SSGテストの例をご覧ください。
Next.jsの例
pages/posts/[id].tsxtsx
import { createServerSideHelpers } from '@trpc/react-query/server';import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';import { appRouter } from 'server/routers/_app';import superjson from 'superjson';import { trpc } from 'utils/trpc';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 prefetchedreturn <>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></>);}
pages/posts/[id].tsxtsx
import { createServerSideHelpers } from '@trpc/react-query/server';import { GetServerSidePropsContext, InferGetServerSidePropsType } from 'next';import { appRouter } from 'server/routers/_app';import superjson from 'superjson';import { trpc } from 'utils/trpc';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 prefetchedreturn <>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></>);}