跳至主内容
版本:11.x

服务端助手

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

服务端助手提供了一组辅助函数,可用于在服务器上预取查询。这对于 SSG(静态站点生成)非常有用,同时若您选择不使用 ssr: true,对 SSR(服务端渲染)也很有帮助。

通过服务端助手进行预取,可以在服务器端填充查询缓存,这意味着这些查询在客户端初始渲染时无需再次获取。

有两种使用服务端助手的方式

1. 内部路由

当您可以直接访问 tRPC 路由时使用此方法,例如在开发单体式 Next.js 应用时。

使用助手会使 tRPC 直接在服务器上调用您的操作过程,无需 HTTP 请求,类似于服务端调用。这也意味着您无法像通常那样访问请求(req)和响应(res)对象。请确保实例化服务端助手时使用不包含 reqres 的上下文(这些通常通过上下文创建过程填充)。在此场景下,我们推荐使用“内部”与“外部”上下文的概念。

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 客户端类似,包含您所有路由作为键名。但不同于 useQueryuseMutation,您将获得 prefetchfetchprefetchInfinitefetchInfinite 函数。

prefetchfetch 的核心区别在于:fetch 的行为类似普通函数调用,会返回查询结果;而 prefetch 不返回结果且永不抛出错误——若需要此行为,请改用 fetchprefetch 会将查询加入缓存,后续通过脱水(dehydrate)操作发送至客户端。

ts
return {
props: {
// very important - use `trpcState` as the key
trpcState: helpers.dehydrate(),
},
};
ts
return {
props: {
// very important - use `trpcState` as the key
trpcState: helpers.dehydrate(),
},
};

经验法则是:对客户端需要使用的查询使用 prefetch,对需在服务端使用结果的查询使用 fetch

这些函数都是 react-query 的封装器,请查阅其官方文档了解详细用法。

信息

完整示例请参见我们的 E2E SSG 测试案例

Next.js 示例

pages/posts/[id].tsx
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>
</>
);
}
pages/posts/[id].tsx
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>
</>
);
}