tRPC 技能决策树与端到端最小应用实战:trpc-router SKILL 导读
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
导读
本文围绕 tRPC 仓库packages/server/skills/trpc-router/SKILL.md这份「技能路由」(Skill Router)文档展开,它不直接教你某一项 tRPC 功能,而是为开发者提供一个按任务分流的决策树:你要做服务端、还是宿主适配器、还是客户端、还是接入框架,分别该加载哪份专项技能文档。同时它给出了一份最小可运行应用的端到端代码骨架(server/trpc.ts → appRouter → standalone 服务器 → 客户端)。读完本文,你将能快速判断自己当前任务属于 tRPC 的哪个环节、该查阅哪个专项 SKILL,并据此在一分钟内搭出第一个类型安全的 query/mutation 应用。
这份文档的定位:全部 tRPC 技能的入口
trpc-router/SKILL.md 是整个 tRPC 技能体系的入口文档(front matter 中标注type: core、library: trpc、library_version: '11.16.0'),其description明确说明:它是所有 tRPC skills 的入口,按任务做决策路由——initTRPC.create()、t.router()、t.procedure、createTRPCClient、适配器、订阅、React Query、Next.js、links、中间件、校验器、错误处理、缓存、FormData 均被覆盖。其官方素材来源为 introduction.mdx 与 quickstart.mdx(见 front matter 的sources)。
因此它是一张「地图」,而同目录下的server-setup、middlewares、validators、error-handling等 15 份 SKILL 才是各自的「城市导游」。使用范式是:先经本决策树定位,再加载对应专项技能。
决策树全景:按「你正在做什么」导航
SKILL.md 的核心是一棵以问题为导向的决策树,本节逐层展开并标注每条分支应加载的专项技能文件(均位于packages/server/skills/目录下)。
分支一:定义 tRPC 后端(服务端)
| 你在做的事 | 加载的技能 |
|---|---|
初始化 tRPC、定义 routers / procedures / context、导出AppRouter | server-setup→ server-setup/SKILL.md |
添加中间件(.use)、鉴权守卫、日志、基础 procedure | middlewares→ middlewares/SKILL.md |
| 使用 Zod 等库做输入/输出校验 | validators→ validators/SKILL.md |
| 抛出类型化错误、为客户端格式化错误、全局错误处理 | error-handling→ error-handling/SKILL.md |
| 在服务端代码中调用 procedure、编写集成测试 | server-side-calls→ server-side-calls/SKILL.md |
| 在 query 响应上设置 Cache-Control 头(CDN / 浏览器缓存) | caching→ caching/SKILL.md |
| 在 mutation 中接收 FormData、File、Blob 或二进制上传 | non-json-content-types→ non-json-content-types/SKILL.md |
| 搭建实时订阅(SSE 或 WebSocket) | subscriptions→ subscriptions/SKILL.md |
分支二:宿主 tRPC API(适配器)
| 你在做的事 | 加载的技能 |
|---|---|
| Node.js 内置 HTTP 服务器(最简单,适合本地开发) | adapter-standalone→ adapter-standalone/SKILL.md |
| Express 中间件 | adapter-express→ adapter-express/SKILL.md |
| Fastify 插件 | adapter-fastify→ adapter-fastify/SKILL.md |
| AWS Lambda(API Gateway v1/v2、Function URLs) | adapter-aws-lambda→ adapter-aws-lambda/SKILL.md |
| Fetch API / Edge(Cloudflare Workers、Deno、Vercel Edge、Astro、Remix) | adapter-fetch→ adapter-fetch/SKILL.md |
对应源码位于 packages/server/src/adapters/,每类宿主均有独立实现文件与文档中成对出现的 example(如 examples/express-minimal、examples/cloudflare-workers、examples/lambda-url 等),便于对照学习。
分支三:消费 tRPC API(客户端)
| 你在做的事 | 加载的技能 |
|---|---|
| 创建 vanilla TypeScript 客户端,配置 links、headers、类型 | client-setup→ client-setup/SKILL.md |
| 配置 link 链(batching、streaming、split、WebSocket、SSE) | links→ links/SKILL.md |
| 用 SuperJSON transformer 支持 Date、Map、Set、BigInt | superjson→ superjson/SKILL.md |
分支四:与框架配合使用
| 你在做的事 | 加载的技能 |
|---|---|
| React + TanStack Query(useQuery、useMutation、queryOptions) | react-query-setup→ react-query-setup/SKILL.md |
| Next.js App Router(RSC、Server Components、HydrateClient) | nextjs-app-router→ nextjs-app-router/SKILL.md |
| Next.js Pages Router(withTRPC、SSR、SSG helpers) | nextjs-pages-router→ nextjs-pages-router/SKILL.md |
分支五:进阶模式
| 你在做的事 | 加载的技能 |
|---|---|
| 从 tRPC router 生成 OpenAPI 规范与 REST 客户端 | openapi→ openapi/SKILL.md |
| 多服务网关、自定义路由 link、SOA(面向服务架构) | service-oriented-architecture→ service-oriented-architecture/SKILL.md |
| 鉴权中间件 + 客户端 headers + 订阅鉴权 | auth→ auth/SKILL.md |
使用技巧:多数任务并非单一分支。例如「搭建一个带鉴权、走 Express 的 React 全栈应用」,决策路径是
server-setup → middlewares → adapter-express → react-query-setup,一次定位后按顺序加载即可。
Quick Reference:最小可运行应用逐段拆解
SKILL.md 的「Quick Reference: Minimal Working App」章节给出了从零到可调用的四段代码。这里逐段还原,并结合仓库中 examples/minimal 的真实工程进一步说明其运行语义。
第 1 段:初始化根对象(server/trpc.ts)
// server/trpc.ts import { initTRPC } from '@trpc/server'; const t = initTRPC.create(); export const router = t.router; export const publicProcedure = t.procedure;这里initTRPC是@trpc/server导出的单例构造器。查看 initTRPC.ts,其定义是export const initTRPC = new TRPCBuilder()——一个可链式调用的类型构造器,create()是终结操作,返回带有procedure、middleware、router、mergeRouters、createCallerFactory五个成员的根对象(同文件 L180-L213)。
值得注意的实践约束:
- 整个后端只调用一次
initTRPC.create()。examples/minimal/src/server/trpc.ts 中的注释「Initialization of tRPC backend, Should be done only once per backend!」即这一约定,随后将router与publicProcedure重新导出,供各路由文件复用。 - 若需要自定义 context / meta / transformer,可在
create()前链式调用.context<T>()、.meta<T>(),或在create(opts)传入RuntimeConfigOptions。从 initTRPC.ts 可见支持的运行期选项包括transformer(数据序列化,默认defaultTransformer)、errorFormatter、isDev(默认按NODE_ENV !== 'production'推断)、allowOutsideOfServer、defaultMeta、isServer。其中allowOutsideOfServer默认false,若在非服务端环境调用会直接抛错(同文件 L174-L179),这是为了防止把 tRPC 服务端代码误引入客户端 bundle。
第 2 段:定义 AppRouter(server/appRouter.ts)
// server/appRouter.ts import { z } from 'zod'; import { publicProcedure, router } from './trpc'; export const appRouter = router({ hello: publicProcedure .input(z.object({ name: z.string() })) .query(({ input }) => ({ greeting: `Hello ${input.name}` })), }); export type AppRouter = typeof appRouter;要点:
- procedure 分为 query / mutation / subscription 三种类型。客户端侧的调用方法由类型自动推导:query 对应
.query()、mutation 对应.mutate()、subscription 对应.subscribe()——这一点可在 createTRPCClient.ts 的DecorateProcedure类型映射中得到源码级印证。 - 输入校验用
.input(z.object(...)),运行时由 Zod schema 把关,类型层面则被自动推导进 resolver 的{ input }参数。 export type AppRouter = typeof appRouter;是类型安全的枢纽:它只导出类型而不导出值,从而可以安全地在前端 import 而不会把服务端代码带进客户端 bundle(这也是官方推荐的约定)。
真实的更完整形态可参考 examples/minimal/src/server/index.ts:其中路由被组织为嵌套命名空间(user.list、user.byId、user.create、examples.iterable),演示了 input 驱动的 query、含 body 的 mutation,以及 async generator 实现的流式 iterable——可见 router 对象天然支持任意嵌套结构,路径即 key 的层级组合。
第 3 段:用 standalone 适配器启动服务(server/index.ts)
// server/index.ts import { createHTTPServer } from '@trpc/server/adapters/standalone'; import { appRouter } from './appRouter'; const server = createHTTPServer({ router: appRouter }); server.listen(3000);createHTTPServer由 standalone.ts 实现:它本质上是http.createServer(createHTTPHandler(opts))的封装——先由createHTTPHandler把 tRPC 请求处理逻辑包成 Node 标准的RequestListener,再交给 Node 内置http模块。在 examples/minimal/src/server/index.ts 中可以看到完全一致的使用方式,这也是官方标注「最简单、适合本地开发」的宿主选择。
适配器可配置项(同文件 L30-L44)值得留意:
router:必填的 AppRouter 实例。basePath:请求路径前缀,默认'/',会被从请求 pathname 头部切掉,例如设为'/trpc/'后所有请求都从/trpc/<procedurePath>进入。该逻辑见 standalone.ts 中basePath ?? '/'与pathname.slice(sliceLength)。- 同时透传
node-http层的所有选项,如createContext(按请求构建 context)、middleware、batching、maxBodySize、响应头自定义等。 - 文件同样导出了
createHTTP2Handler(同文件 L119-L120),面向需要 HTTP/2 的场景。
第 4 段:创建类型安全客户端(client/index.ts)
// client/index.ts import { createTRPCClient, httpBatchLink } from '@trpc/client'; import type { AppRouter } from '../server/appRouter'; const trpc = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url: 'http://localhost:3000' })], }); const result = await trpc.hello.query({ name: 'World' });两个值得展开的细节:
createTRPCClient<AppRouter>的类型魔法:查看 createTRPCClient.ts 可知,其内部基于TRPCUntypedClient之上套了双层 Proxy(createFlatProxy+createRecursiveProxy,同文件 L144-L151),把trpc.hello.query(...)这种链式路径在运行时拆成path = 'hello'、type = 'query'后转发给底层客户端;而在编译期,TRPCClient<TRouter>会把AppRouter的每个叶子 procedure 精确装饰为query/mutate/subscribe方法并推断出各自的 input/output 类型。入参写错或返回类型不匹配都会在编辑器里直接报红——这就是「端到端类型安全」的实现基础。httpBatchLink默认开启请求批处理:多个在同一事件循环 tick 内发起的 query 会合并成一个 HTTP 请求(通过dataLoader实现,见 httpBatchLink.ts),从而减少网络往返。其配置项包括url(必填)、headers(对象或函数)、transformer,以及maxURLLength/maxItems(默认均为Infinity,用于在 URL 过长或批次数超限时自动拆分请求,见同文件 L25-L26 与 L33-L53 的validate逻辑)。同时注意:该 link不支持 subscription,一旦收到 subscription 操作会直接抛出「Subscriptions are unsupported byhttpLink- usehttpSubscriptionLinkorwsLink」的错误(同文件 L96-L99)。
何时不需要这份决策树:任务与技能的快速对应表
把决策树压缩成一张速查表,可帮助 Agent 与开发者以更少跳转命中目标技能:
| 任务关键词 | 首选技能 | 仓库路径 |
|---|---|---|
| 初始化 / router / procedure / context | server-setup | packages/server/skills/server-setup/ |
| middleware / 鉴权 / 日志 | middlewares | packages/server/skills/middlewares/ |
| Zod / 输入校验 | validators | packages/server/skills/validators/ |
| 错误 / 格式化 | error-handling | packages/server/skills/error-handling/ |
| caller / 测试 | server-side-calls | packages/server/skills/server-side-calls/ |
| Cache-Control | caching | packages/server/skills/caching/ |
| FormData / 上传 | non-json-content-types | packages/server/skills/non-json-content-types/ |
| SSE / WebSocket | subscriptions | packages/server/skills/subscriptions/ |
| standalone / express / fastify / lambda / fetch 宿主 | adapter-* 系列 | packages/server/skills/adapter-*/ |
| 创建客户端 / links / transformer | client-setup / links / superjson | packages/client/skills/*/ |
| React + TanStack Query | react-query-setup | packages/tanstack-react-query/skills/react-query-setup/ |
| Next.js App / Pages Router | nextjs-app-router / nextjs-pages-router | packages/next/skills/*/ |
| OpenAPI 生成 | openapi | packages/openapi/skills/openapi/ |
| 多服务 / SOA | service-oriented-architecture | packages/server/skills/service-oriented-architecture/ |
从决策树走向真实工程:下一步实践建议
有了上面的地图与最小骨架,建议按以下路径把「能跑」升级为「能上生产」:
- 先跑通最小应用:参照 examples/minimal(内含
server/trpc.ts、server/index.ts、server/db.ts与类型共享的shared/transformer.ts),或直接套用本 SKILL 的四段代码。 - 按需补 context 与 middleware:加载
server-setup与middlewares两份 SKILL,把鉴权、日志、租户隔离等横切逻辑收敛进基础 procedure。 - 为每个路由对象套上校验:在
.input()处定义 Zod schema(更复杂输入可参考validators中多 schema 组合的用法)。 - 挑选符合部署形态的宿主:本地/内部用 standalone;对外服务按平台选 express、fastify、lambda 或 fetch 适配器,各 SKILL 目录与其在 examples 中的同名示例一一对应,可直接对照。
- 把 transformer、错误格式化等跨端选项固化:在
initTRPC.create({ transformer, errorFormatter })中统一声明,服务端与客户端两侧需保持一致。
See Also:配套技能直达
SKILL.md 结尾给出的配套技能均可在仓库内直接查看:
server-setup—— 完整的服务端初始化细节:server-setup/SKILL.mdclient-setup—— 完整的客户端配置:client-setup/SKILL.mdadapter-standalone—— 上手最快的适配器:adapter-standalone/SKILL.mdreact-query-setup—— React 集成:react-query-setup/SKILL.mdnextjs-app-router—— Next.js App Router 集成:nextjs-app-router/SKILL.md
官方文档(也是该 SKILL 的sources)同样在仓库内可查阅:introduction.mdx 与 quickstart.mdx。想要真正理解这份决策树为何如此划分,可进一步对照服务端核心实现 initTRPC.ts、客户端代理机制 createTRPCClient.ts 与批处理链路 httpBatchLink.ts,三者恰好构成「路由对象 → 类型推导客户端 → 高效传输」的完整闭环。
【免费下载链接】trpc🧙♀️ Move Fast and Break Nothing. End-to-end typesafe APIs made easy.项目地址: https://gitcode.com/GitHub_Trending/tr/trpc
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考