
Next.js Edge Runtime 接入 tRPC基于 fetch 适配器构建端到端类型安全 API【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc本指南以仓库内examples/next-edge-runtime示例为主体讲解如何在 Next.js 的Edge Runtime而非默认 Node.js Serverless 运行时下使用 tRPC 的 fetch 适配器fetchRequestHandler提供 API 路由并借助createTRPCNext在 React 页面中消费类型安全的查询。读完本文你将掌握 Edge Runtime tRPC 的完整搭建流程、目录结构与各核心文件职责并了解它与使用 Next.js 原生适配器的next-minimal-starter示例之间的差异以及 fetch 适配器背后的底层实现原理。一、示例定位同一个 starter换一个运行时在正式动手之前先明确这个示例在整个仓库中的位置仓库根目录的 examples 下收集了大量可直接运行的端到端示例。next-edge-runtime官方 README 说明指出它与 next-minimal-starter 示例 在业务代码上完全一致唯一区别是它运行在 Next.js Edge Runtime 上并且服务端使用的是 tRPC 的 fetch 适配器。也就是说两个示例共享同一套“最小可运行”骨架src/server/trpc.ts初始化 tRPC暴露publicProcedure与routersrc/pages/api/trpc/[trpc].ts定义 API 处理器并承载所有 API 路由src/utils/trpc.ts创建客户端提供trpc对象供 React 组件使用src/pages/_app.tsx与src/pages/index.tsx挂载 Provider 并在页面中发起带类型的查询。核心差异集中在[trpc].ts这一文件上我们将在后文专门对比。二、环境与快速启动运行该示例需要 Node.js 与 npm/pnpm 环境示例通过create-next-app的--example参数直接从本仓库拉取对应目录生成新项目。2.1 初始化与安装按照 README 中的命令操作npx create-next-app --example https://github.com/trpc/trpc --example-path examples/next-edge-runtime trpc-next-edge-runtime cd trpc-next-edge-runtime npm i npm run dev首行命令会以仓库中的examples/next-edge-runtime为模板在当前目录下创建名为trpc-next-edge-runtime的 Next.js 项目npm i安装依赖其中核心依赖包括trpc/client、trpc/next、trpc/react-query、trpc/server以及next、react、zod等见 package.jsonnpm run dev启动开发服务器。2.2 项目内直接运行如果你希望直接在仓库内体验先安装仓库根依赖pnpm workspace随后进入该示例目录执行npm run dev # 等价于 next dev见 package.json 中 scripts.dev若以生产模式运行可执行npm run build后再npm run start对应next build与next start。注意 next.config.ts 中配置了eslint: { ignoreDuringBuilds: !!process.env.CI }即在 CI 环境构建时跳过 ESLint 校验因为该示例把 lint 作为独立的 CI 任务执行。三、服务端Edge Runtime 下的 tRPC 路由3.1 初始化 tRPC 根配置src/server/trpc.ts 是所有服务端逻辑的入口其职责是一次性完成 tRPC 的初始化import { initTRPC } from trpc/server; const t initTRPC.create(); /** * Unprotected procedure **/ export const publicProcedure t.procedure; export const router t.router;关键点如下initTRPC在每个应用中只能调用一次这里只导出实际使用的能力publicProcedure与router从而在项目层强制约束“应该使用哪种基础 procedure”便于未来引入受保护的中间件流程时统一控制后续业务中如需扩展受保护的 procedure通常会在同层再导出protectedProcedure叠加鉴权中间件可参考仓库 server 文档 与 middlewares 技能文档。3.2 API 路由与 fetch 适配器src/pages/api/trpc/[trpc].ts 是 pages router 下捕获/api/trpc/*全部请求的动态路由。整个文件可以分为三部分1定义 AppRouter 与首个 procedureimport { fetchRequestHandler } from trpc/server/adapters/fetch; import { publicProcedure, router } from ~/server/trpc; import type { NextRequest } from next/server; import { z } from zod; const appRouter router({ greeting: publicProcedure .input( z.object({ name: z.string().nullish(), }), ) .query(({ input }) { return { text: hello ${input?.name ?? world}, }; }), }); export type AppRouter typeof appRouter;greeting是一个公开 query procedure其输入用zod声明为{ name?: string | null }input会被 tRPC 在运行时校验同时类型信息会被提取到AppRouter类型中并贯穿到客户端只导出AppRouter类型而不导出实现客户端永远不会接触到服务端实际逻辑仅能拿到类型契约。2切换 Edge Runtimeexport const config { runtime: edge, };这是本示例与next-minimal-starter的关键差异点通过导出config.runtime edge让该路由在Edge Runtime上执行函数在 Vercel Edge Network 的边缘节点运行而非传统 Node.js Serverless 环境。3用 fetch 适配器导出 handlerexport default async function handler(req: NextRequest) { return fetchRequestHandler({ endpoint: /api/trpc, router: appRouter, req, createContext: () ({}), }); }handler接收标准NextRequest直接返回Response这与 Next.js Route Handlers / Edge 函数的要求完全一致endpoint告知适配器挂载路径前缀此处为/api/trpc用于把 URL 中剩余部分解析为具体的 procedure 调用路径如/api/trpc/greetingcreateContext: () ({})目前返回空上下文实际项目中通常在此处解析认证头、注入数据库连接等fetch 适配器的createContext会收到{ req, resHeaders, info, ... }其中req即原始请求对象可读取headers、cookies等返回的 Promise 直接作为 HTTP 响应返回给客户端。3.3 fetch 适配器的底层实现从源码层面看fetchRequestHandler实现在 packages/server/src/adapters/fetch/fetchRequestHandler.ts。其核心流程结合源码可推断大致为对opts.req.url构造URL对象通过trimSlashes归一化两端斜杠用url.pathname减去endpoint前缀得到剩余路径作为要调用的 procedure 路径path包装createContext将req、resHeaders及info等注入其中调用内部resolveResponse位于trpc/server/http统一解析 HTTP 请求返回Response对象支持可选onError回调与responseMeta回调后者可向响应追加自定义头或覆盖状态码。正是由于适配器只依赖 Web 标准的Request/Response/URL/Headers它才可以被复用于 Cloudflare Workers、Bun、Deno、Vercel Edge 等一切实现 Fetch API 的运行时——这也是本示例选择它来对接 Edge Runtime 的根本原因。四、客户端createTRPCNext 的类型安全消费4.1 创建客户端实例src/utils/trpc.ts 负责创建客户端import { httpBatchLink } from trpc/client; import { createTRPCNext } from trpc/next; import type { AppRouter } from ../pages/api/trpc/[trpc]; function getBaseUrl() { if (typeof window ! undefined) { // In the browser, we return a relative URL return ; } // When rendering on the server, we return an absolute URL // reference for vercel.com if (process.env.VERCEL_URL) { return https://${process.env.VERCEL_URL}; } // assume localhost return http://localhost:${process.env.PORT ?? 3000}; } export const trpc createTRPCNextAppRouter({ config() { return { links: [ httpBatchLink({ url: getBaseUrl() /api/trpc, }), ], }; }, });几个值得注意的细节客户端通过createTRPCNextAppRouter()获得与AppRouter类型联动的 hooks服务端新增 procedure 或修改返回结构客户端会立即出现类型提示甚至编译错误基础 URL 的运行时判定浏览器端使用相对 URL同源直接命中/api/trpc服务端渲染时则根据环境变量给出绝对地址——部署在 Vercel 时优先使用VERCEL_URL构造https://deployment.vercel.app本地开发则回退到http://localhost:${PORT ?? 3000}。这种 SSR 期间需要向自身发起请求的场景正是 tRPC 官方客户端文档推荐的写法使用了httpBatchLink把一段时间内多个并发请求合并为单个 HTTP 请求从而减少网络往返该 Link 默认即支持。4.2 挂载 Provider 并调用查询src/pages/_app.tsx 通过trpc.withTRPC(MyApp)包裹整个应用自动完成 React Query 的 QueryClient 与 Provider 注入import type { AppType } from next/app; import { trpc } from ../utils/trpc; const MyApp: AppType ({ Component, pageProps }) { return Component {...pageProps} /; }; export default trpc.withTRPC(MyApp);页面组件 src/pages/index.tsx 中直接以 hook 形式调用服务端 procedureimport { trpc } from ../utils/trpc; export default function IndexPage() { const result trpc.greeting.useQuery({ name: client }); if (!result.data) { return h1Loading.../h1; } return h1{result.data.text}/h1; }trpc.greeting.useQuery({ name: client })的入参类型由服务端 zod schema 推导而来多传、漏传都会触发类型错误result.data的类型为{ text: string }与 [trpc].ts 中greeting的返回值严格一致在编辑器中对greeting、text使用 “Go to Definition” 可以直接跳到服务端定义使用 “Rename Symbol” 可以同时重命名客户端与服务端两端的符号。五、与 next-minimal-starter 的对比两条适配器路线正如前文所述本示例与 next-minimal-starter 几乎完全相同差异只在[trpc].ts的处理器部分。对比维度next-edge-runtimenext-minimal-starter运行时Edge Runtimeconfig.runtime edge默认 Node.js Serverless 运行时使用的适配器trpc/server/adapters/fetch的fetchRequestHandlertrpc/server/adapters/next的createNextApiHandler处理器形态async (req: NextRequest) PromiseResponse(req, res) voidNextApiHandler适用目标边缘节点、冷启动敏感的低延迟场景需要 Node API如原生 WebSocket、依赖 Node 特有模块的场景对照 next-minimal-starter 的 [trpc].ts 可以看到后者直接使用trpcNext.createNextApiHandler({ router, createContext })导出 Next.js 原生的 API handler且示例中还额外演示了subscription类型的 procedure。需要留意的是Edge Runtime 对运行时能力有硬性约束它只暴露部分 Web API不支持 Node.js 的内置模块如fs、stream与某些第三方依赖。因此若 procedure 中需要访问 Node 专属能力应继续使用trpc/server/adapters/next并移除runtime: edge配置若目标是最大化边缘部署优势就近响应、快速冷启动则应遵循本示例的 fetch 适配器路线并确保所有依赖与代码都兼容 Edge Runtime。从仓库源码结构看tRPC 为每个目标运行时都准备了独立适配器实现均位于 packages/server/src/adapters 目录下包括next、fetch、aws-lambda、express、fastify、standalone等这说明“同一套 Router 定义 按平台选择适配器”正是 tRPC 服务端设计的核心思路。六、实践建议与扩展方向基于上面的剖析给出几条可直接落地的经验路由如何扩展直接在router({...})中追加新的 procedure导出类型后客户端即可立刻获得类型提示示例注释中也提供了被注释的getUser模板可供参考。输入校验继续用 zodgreeting展示了.input(z.object(...))的最小范式仓库其他示例如 next-formdata、next-prisma-starter展示了更复杂的校验与文件上传场景。Edge 场景下的上下文设计createContext中应避免使用 Node 专属库基于请求头/ Cookie 做鉴权时使用标准Headers/RequestAPI 即可。错误处理与响应头若需要在边缘层统一注入响应头可使用fetchRequestHandler的responseMeta选项onError则可用于错误日志上报两者在 fetchRequestHandler 源码 中均有体现。把示例跑起来做实验next-edge-runtime自带可交互的 React 页面推荐在编辑器内对greeting的输入 schema 或返回值做改动直观感受服务端改动如何即时传导到客户端的类型系统——这是端到端类型安全最直接的“教学现场”。小结本示例演示了一种轻量且可移植的 tRPC 服务端接入方式在 Next.js pages router 中导出一个运行于 Edge Runtime 的动态路由再用fetchRequestHandler把标准Request解析为对AppRouter中 procedure 的调用。由于该适配器只依赖 Web 标准 API同一份 Router 定义未来也能平移到 Cloudflare Workers、Bun、Deno 等其他 Fetch 运行时。配合createTRPCNext生成的类型安全 hooks从服务端 schema 到 React 页面的全链路类型推导就此闭环。【免费下载链接】trpc♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考