在 Next.js 中进行设置
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
推荐文件结构
我们推荐采用这样的文件结构,虽然 tRPC 并不强制要求。你可以在我们的示例中看到这种结构。本页剩余部分将引导你完成将 tRPC 集成到此结构中的过程。
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└── [..]
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└── [..]
在现有 Next.js 项目中集成 tRPC
1. 安装依赖
- npm
- yarn
- pnpm
- bun
shnpm install @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shnpm install @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shyarn add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shyarn add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shpnpm add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shpnpm add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shbun add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
shbun add @trpc/server @trpc/client @trpc/react-query @trpc/next @tanstack/react-query@4 zod
Next.js 集成实际上是我们的 React Query 集成与 Next.js 特定功能的结合。
2. 启用严格模式
若使用 Zod 进行输入验证,需在 tsconfig.json 中启用严格模式:
tsconfig.jsondiff"compilerOptions": {+ "strict": true}
tsconfig.jsondiff"compilerOptions": {+ "strict": true}
如果严格模式限制过强,你至少需要启用 strictNullChecks:
tsconfig.jsondiff"compilerOptions": {+ "strictNullChecks": true}
tsconfig.jsondiff"compilerOptions": {+ "strictNullChecks": true}
3. 创建 tRPC 路由
在 src/server/trpc.ts 中使用 initTRPC 函数初始化 tRPC 后端,并创建你的第一个路由器。我们将在此创建一个简单的 "hello world" 路由器和过程 - 关于创建 tRPC API 的深入信息,请参考:
-
关于在 Next.js 服务器中挂载 tRPC 的 Next.js 适配器文档
View sample backend
server/trpc.tstsimport { 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 helpersexport const router = t.router;export const procedure = t.procedure;
server/trpc.tstsimport { 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 helpersexport const router = t.router;export const procedure = t.procedure;
server/routers/_app.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
server/routers/_app.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
pages/api/trpc/[trpc].tstsimport * as trpcNext from '@trpc/server/adapters/next';import { appRouter } from '../../../server/routers/_app';// export API handler// @link https://trpc.io/docs/server/adaptersexport default trpcNext.createNextApiHandler({router: appRouter,createContext: () => ({}),});
pages/api/trpc/[trpc].tstsimport * as trpcNext from '@trpc/server/adapters/next';import { appRouter } from '../../../server/routers/_app';// export API handler// @link https://trpc.io/docs/server/adaptersexport default trpcNext.createNextApiHandler({router: appRouter,createContext: () => ({}),});
4. 创建 tRPC 钩子
使用 createTRPCNext 函数基于你的 API 类型签名创建一组强类型钩子。
utils/trpc.tstsximport { 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 pathreturn '';if (process.env.VERCEL_URL)// reference for vercel.comreturn `https://${process.env.VERCEL_URL}`;if (process.env.RENDER_INTERNAL_HOSTNAME)// reference for render.comreturn `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;// assume localhostreturn `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/ssr**/url: `${getBaseUrl()}/api/trpc`,// You can pass any HTTP headers you wish hereasync headers() {return {// authorization: getAuthCookie(),};},}),],};},/*** @see https://trpc.io/docs/ssr**/ssr: false,});
utils/trpc.tstsximport { 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 pathreturn '';if (process.env.VERCEL_URL)// reference for vercel.comreturn `https://${process.env.VERCEL_URL}`;if (process.env.RENDER_INTERNAL_HOSTNAME)// reference for render.comreturn `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;// assume localhostreturn `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/ssr**/url: `${getBaseUrl()}/api/trpc`,// You can pass any HTTP headers you wish hereasync headers() {return {// authorization: getAuthCookie(),};},}),],};},/*** @see https://trpc.io/docs/ssr**/ssr: false,});
createTRPCNext 无法在 tRPC v9 的互操作模式下使用。若你正通过互操作模式从 v9 迁移,应继续使用旧版初始化 tRPC 的方式。
5. 配置 _app.tsx
将根应用页面包裹在 trpc.withTRPC 高阶组件中,类似如下:
pages/_app.tsxtsximport type { AppType } from 'next/app';import { trpc } from '../utils/trpc';const MyApp: AppType = ({ Component, pageProps }) => {return <Component {...pageProps} />;};export default trpc.withTRPC(MyApp);
pages/_app.tsxtsximport type { AppType } from 'next/app';import { trpc } from '../utils/trpc';const MyApp: AppType = ({ Component, pageProps }) => {return <Component {...pageProps} />;};export default trpc.withTRPC(MyApp);
6. 发起 API 请求
全部设置完成!
现在即可使用刚创建的 React 钩子调用 API。更多细节请参阅 React Query 集成
pages/index.tsxtsximport { 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>);}
pages/index.tsxtsximport { 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() 的选项
config 回调函数
config 参数为返回配置对象的函数,用于设置 tRPC 和 React Query 客户端。该函数接收包含 Next.js req 对象等信息的 ctx 参数,返回对象支持以下属性:
-
必需:
-
links:用于定制 tRPC 客户端与服务器间的数据流。了解更多 -
可选:
-
queryClientConfig:tRPC React 钩子内部使用的 React QueryQueryClient配置对象:QueryClient 文档 -
queryClient: React Query 的 QueryClient 实例- 注意: 只能选择提供
queryClient或queryClientConfig其中一项
- 注意: 只能选择提供
-
transformer: 应用于输出数据的转换器。了解更多关于数据转换器的信息 -
abortOnUnmount: 决定组件卸载时是否取消进行中的请求。默认为false
overrides:(默认值:undefined)
ssr-布尔值(默认:false)
控制 tRPC 在服务端渲染页面时是否等待查询完成,默认值为 false。
responseMeta-回调函数
用于在服务端渲染时设置响应头与 HTTP 状态码。
示例
utils/trpc.tstsximport { createTRPCNext } from '@trpc/next';import type { AppRouter } from '../pages/api/trpc/[trpc]';export const trpc = createTRPCNext<AppRouter>({config(config) {/* [...] */},ssr: true,responseMeta(opts) {const { clientErrors } = opts;if (clientErrors.length) {// propagate first http error from API callsreturn {status: clientErrors[0].data?.httpStatus ?? 500,};}// cache full page for 1 day + revalidate once every secondconst ONE_DAY_IN_SECONDS = 60 * 60 * 24;return {'Cache-Control': `s-maxage=1, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,};},});
utils/trpc.tstsximport { createTRPCNext } from '@trpc/next';import type { AppRouter } from '../pages/api/trpc/[trpc]';export const trpc = createTRPCNext<AppRouter>({config(config) {/* [...] */},ssr: true,responseMeta(opts) {const { clientErrors } = opts;if (clientErrors.length) {// propagate first http error from API callsreturn {status: clientErrors[0].data?.httpStatus ?? 500,};}// cache full page for 1 day + revalidate once every secondconst ONE_DAY_IN_SECONDS = 60 * 60 * 24;return {'Cache-Control': `s-maxage=1, stale-while-revalidate=${ONE_DAY_IN_SECONDS}`,};},});