关于 trpc
tRPC 是面向 TypeScript 全栈应用的类型安全 API 框架,通过共享 TypeScript 类型连接服务器与客户端,无需代码生成或 schema。它适合需要快速迭代 API、跨端共享类型或实时订阅的项目。
主要功能
- 01共享类型推导 API
- 02查询、变更与订阅过程
- 03输入与输出校验
- 04中间件和请求上下文
- 05React 与原生客户端支持
- 06批处理及多种传输链路
适用场景
- 01为 TypeScript 全栈应用构建 API
- 02在 monorepo 中共享 API 类型
- 03实现 WebSocket 或 SSE 实时订阅
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ oakoss/ agent-skills --skill trpc安装后在 Claude Code 中输入/trpc确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/adapter-setup.mderror-handling.mdmiddleware-context.mdreact-integration.mdrouter-procedures.mdserver-callers.mdsubscriptions.mdtesting-patterns.md详细介绍
tRPC
概述
tRPC 通过在服务端和客户端之间共享 TypeScript 类型,无需代码生成或架构定义,即可实现端到端的类型安全 API。服务端在路由器中定义过程(查询、变更、订阅),客户端则从导出的 AppRouter 类型推断所有类型。
适用场景: 需要类型安全 API 层的 TypeScript 全栈应用、共享类型的单体代码库、实时订阅,以及无需承担 OpenAPI/GraphQL 开销即可快速迭代 API 的场景。
不适用场景: 由非 TypeScript 客户端调用的公共 API(请使用 REST/GraphQL)、多语言后端,以及需要将生成 OpenAPI 规范作为主要输出的项目。
快速参考
| 模式 | API | 要点 |
|---|---|---|
| 初始化 | initTRPC.context<Ctx>().create() | 单一入口,只需配置一次 |
| 路由器 | t.router({ ... }) | 使用 t.mergeRouters() 嵌套路由器 |
| 查询 | t.procedure.input(schema).query(fn) | 读取操作,由客户端缓存 |
| 变更 | t.procedure.input(schema).mutation(fn) | 写入操作 |
| 订阅 | t.procedure.subscription(fn) | 通过 WebSocket 或 SSE 实现实时通信 |
| 中间件 | t.middleware(fn) | 使用 .use() 串联,通过 next({ ctx }) 扩展上下文 |
| 独立中间件 | experimental_standaloneMiddleware<{}>() | 可复用,并带有显式类型约束 |
| 上下文 | createContext({ req, res }) | 每个请求独立创建,并传递给所有过程 |
| 服务端调用器 | t.createCallerFactory(router) | 类型安全的服务端过程调用 |
| 错误 | throw new TRPCError({ code, message }) | 映射为 HTTP 状态码 |
| 错误格式化器 | initTRPC.create({ errorFormatter }) | 自定义错误结构,公开 Zod 错误 |
| 输入验证 | .input(zodSchema) | 多次调用 .input() 会合并(取交集) |
| 输出验证 | .output(zodSchema) | 从响应中剔除多余字段 |
| React 钩子 | trpc.useQuery() / trpc.useMutation() | 基于 @tanstack/react-query 构建 |
| React 工具 | trpc.useUtils() | 使缓存失效、预取数据、设置缓存中的数据 |
| Suspense 查询 | trpc.useSuspenseQuery() | 兼容 Suspense 的数据获取 |
| React 订阅 | trpc.useSubscription(input, opts) | 通过 onData 回调处理实时事件 |
| 原生客户端 | createTRPCClient<AppRouter>({ links }) | 不依赖 React |
| 批处理链接 | httpBatchLink({ url }) | 在单个 HTTP 调用中批处理请求 |
| 流式链接 | httpBatchStreamLink({ url }) | 随响应就绪逐步传输响应 |
| WS 链接 | wsLink({ client: wsClient }) | 为订阅提供 WebSocket 传输 |
| 拆分链接 | splitLink({ condition, true, false }) | 将操作路由到不同的传输方式 |
| 日志记录链接 | loggerLink() | 在开发环境中调试请求和响应 |
| 数据转换器 | initTRPC.create({ transformer }) | 序列化 Dates、Maps、Sets(superjson) |
| Fetch 适配器 | fetchRequestHandler({ endpoint, req }) | 适用于 Cloudflare Workers、Deno、Bun、Next.js |
| 过程链式调用 | publicProcedure.use(auth).use(rateLimit) | 构建可复用的过程基础 |
常见错误
| 错误 | 正确做法 |
|---|---|
导出完整的 t 对象 | 仅导出 t.router、t.procedure、t.middleware、t.createCallerFactory |
| 在 procedure 中创建 context | 在适配器设置中创建 context,并自动传递 |
将 any 用作 context 类型 | 使用 initTRPC.context<MyContext>().create() 定义 context 类型 |
| 在 procedure 中捕获错误 | 让错误继续向上抛出;在适配器中使用 onError 或错误格式化器 |
| 在客户端导入服务器端代码 | 仅导入 type AppRouter,绝不导入运行时服务器端代码 |
在生产环境的请求处理程序中使用 createCaller | 使用 createCallerFactory 创建类型安全且可复用的调用方 |
| 定义输入时不进行验证 | 始终使用 Zod、Valibot 或 ArkType 架构进行验证 |
| 错误地嵌套路由器 | 在键中使用点号表示法或使用 t.mergeRouters(),不要深度嵌套 |
| 客户端和服务器端使用不同的转换器 | 两端必须使用相同的转换器(例如 superjson) |
| 在组件渲染时创建 QueryClient | 在组件外创建一次,或使用 useState 初始化器 |
| 在 hooks 上手动指定 TypeScript 泛型 | 为路由器的 procedures 指定类型,让类型推断自动传递到客户端 |
在 queryFn 中调用 fetch 时不传递 signal | tRPC React 会自动处理中止信号 |
安装
pnpm add @trpc/server @trpc/client @trpc/react-query @tanstack/react-query zod
对于订阅,请添加 ws(服务器端)并配置 wsLink(客户端)。对于基于 SSE 的订阅,请改用 httpBatchStreamLink。
委派
- Procedure 发现:使用
ExploreAgent - 路由器架构审查:使用
TaskAgent - 代码审查:委派给
code-reviewerAgent
如果有
tanstack-query技能,请将查询/变更缓存、失效处理和乐观更新模式委派给它。 如果有zod-validation技能,请将输入架构设计和验证模式委派给它。 如果有drizzle-orm技能,请将 procedure 中的数据库查询模式委派给它。 如果有vitest-testing技能,请将测试运行器配置和断言模式委派给它。 如果有hono技能,请将 Hono 框架路由和中间件模式委派给它。