tRPC 简介 #
什么是 RPC? #
在了解 tRPC 之前,我们需要先理解 RPC(Remote Procedure Call,远程过程调用)的概念。RPC 是一种协议,允许程序调用另一个地址空间(通常在网络上的另一台机器)的过程或函数,就像调用本地函数一样。
text
┌─────────────────────────────────────────────────────────────┐
│ RPC 的本质 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统方式: │
│ │
│ ┌─────────┐ HTTP 请求 ┌─────────┐ │
│ │ 客户端 │ ───────────────> │ 服务端 │ │
│ └─────────┘ └─────────┘ │
│ │
│ 需要手动: │
│ - 定义 API 路由 │
│ - 序列化/反序列化数据 │
│ - 处理类型转换 │
│ - 维护前后端类型一致性 │
│ │
│ RPC 方式: │
│ │
│ ┌─────────┐ 直接调用 ┌─────────┐ │
│ │ 客户端 │ ───────────────> │ 服务端 │ │
│ └─────────┘ server.add() └─────────┘ │
│ │
│ 就像调用本地函数一样! │
│ │
└─────────────────────────────────────────────────────────────┘
传统 API 开发的痛点 #
text
┌─────────────────────────────────────────────────────────────┐
│ 传统开发流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 后端定义接口 │
│ ┌─────────────────────────────────────────┐ │
│ │ // 定义 REST API │ │
│ │ POST /api/users │ │
│ │ GET /api/users/:id │ │
│ │ PUT /api/users/:id │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 2. 编写 API 文档 / Swagger │
│ ┌─────────────────────────────────────────┐ │
│ │ openapi: 3.0.0 │ │
│ │ paths: │ │
│ │ /api/users: │ │
│ │ post: │ │
│ │ requestBody: ... │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 3. 前端定义类型 │
│ ┌─────────────────────────────────────────┐ │
│ │ interface User { │ │
│ │ id: number; │ │
│ │ name: string; │ │
│ │ } │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 4. 前端调用 API │
│ ┌─────────────────────────────────────────┐ │
│ │ const res = await fetch('/api/users'); │ │
│ │ const user = await res.json(); │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 问题: │
│ ❌ 类型定义重复 │
│ ❌ 前后端类型可能不一致 │
│ ❌ API 变更需要同步更新 │
│ ❌ 没有编译时类型检查 │
│ │
└─────────────────────────────────────────────────────────────┘
什么是 tRPC? #
tRPC(TypeScript Remote Procedure Call)是一个用于 TypeScript 的端到端类型安全 RPC 框架。它允许你在不定义任何 schema 的情况下,直接从 TypeScript 前端调用后端函数,并享受完整的类型安全和自动补全。
核心定位 #
text
┌─────────────────────────────────────────────────────────────┐
│ tRPC │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 端到端类型 │ │ 零 Schema │ │ 自动补全 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 轻量级 │ │ 框架无关 │ │ 开发体验 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
tRPC 解决的问题 #
text
┌─────────────────────────────────────────────────────────────┐
│ tRPC 解决的问题 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统 TypeScript 全栈开发痛点: │
│ │
│ 1. 类型同步问题 │
│ 前后端各自定义类型 │
│ API 变更时需要手动同步 │
│ 容易出现类型不一致 │
│ │
│ 2. Schema 维护成本 │
│ 需要维护 OpenAPI/GraphQL Schema │
│ 代码生成步骤繁琐 │
│ 增加项目复杂度 │
│ │
│ 3. 开发效率低下 │
│ 没有自动补全 │
│ 需要查阅 API 文档 │
│ 运行时才能发现错误 │
│ │
│ tRPC 解决方案: │
│ │
│ ✅ 端到端类型安全:前后端共享类型 │
│ ✅ 零 Schema:直接使用 TypeScript 类型 │
│ ✅ 自动补全:IDE 智能提示 │
│ ✅ 编译时检查:提前发现错误 │
│ ✅ 极简开发:像调用本地函数一样 │
│ │
└─────────────────────────────────────────────────────────────┘
tRPC 的历史 #
发展历程 #
text
2020年 ─── tRPC 项目启动
│
│ Alex Johansson 创建
│ 为 TypeScript 全栈开发设计
│
2021年 ─── 开源发布
│
│ GitHub 开源
│ 社区快速增长
│
2022年 ─── 生态扩展
│
│ 支持 Next.js、React、Vue
│ 插件系统完善
│
2023年 ─── 企业采用
│
│ 大量公司采用
│ 生态成熟
│
至今 ─── 行业标准
│
│ GitHub 30k+ Stars
│ TypeScript 全栈首选方案
里程碑版本 #
| 版本 | 时间 | 重要特性 |
|---|---|---|
| 1.0 | 2021 | 初始发布,核心 RPC 功能 |
| 9.0 | 2022 | 稳定版本,完整生态支持 |
| 10.0 | 2023 | 全新 API 设计,性能优化 |
| 11.0 | 2024 | 更好的类型推断,新特性 |
tRPC 的核心特点 #
1. 端到端类型安全 #
text
┌─────────────────────────────────────────────────────────────┐
│ 端到端类型安全 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 后端定义: │
│ ┌─────────────────────────────────────────┐ │
│ │ const router = t.router({ │ │
│ │ user: { │ │
│ │ getById: t.procedure │ │
│ │ .input(z.object({ id: z.number()}))│ │
│ │ .output(z.object({ │ │
│ │ id: z.number(), │ │
│ │ name: z.string() │ │
│ │ })) │ │
│ │ .query(({ input }) => { │ │
│ │ return { id: input.id, name: 'John' }│ │
│ │ }) │ │
│ │ } │ │
│ │ }) │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 前端调用: │
│ ┌─────────────────────────────────────────┐ │
│ │ const user = await trpc.user.getById │ │
│ │ .query({ id: 1 }); │ │
│ │ │ │
│ │ // user 类型自动推断为: │ │
│ │ // { id: number; name: string } │ │
│ │ │ │
│ │ // ✅ 完整的自动补全 │ │
│ │ // ✅ 编译时类型检查 │ │
│ │ // ✅ 重构时自动更新 │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
2. 零 Schema 定义 #
text
┌─────────────────────────────────────────────────────────────┐
│ 零 Schema 设计 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 传统方式(需要 Schema): │
│ │
│ GraphQL: │
│ type User { │
│ id: ID! │
│ name: String! │
│ } │
│ type Query { │
│ user(id: ID!): User │
│ } │
│ │
│ OpenAPI: │
│ paths: │
│ /users/{id}: │
│ get: │
│ parameters: ... │
│ responses: ... │
│ │
│ tRPC 方式(零 Schema): │
│ │
│ const router = t.router({ │
│ user: { │
│ getById: t.procedure │
│ .input(z.object({ id: z.number() })) │
│ .query(({ input }) => { │
│ return db.user.findUnique({ where: { id: input.id }})│
│ }) │
│ } │
│ }) │
│ │
│ ✅ 直接使用 TypeScript/Zod 类型 │
│ ✅ 无需额外定义 Schema │
│ ✅ 类型自动推断和共享 │
│ │
└─────────────────────────────────────────────────────────────┘
3. 框架无关 #
text
┌─────────────────────────────────────────────────────────────┐
│ 框架无关设计 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 服务端支持: │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Next.js │ │ Express │ │ Fastify │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ NestJS │ │ Hono │ │ Node.js │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ 客户端支持: │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ React │ │ Vue │ │ Svelte │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Solid │ │ Next.js │ │ Nuxt │ │
│ └───────────┘ └───────────┘ └───────────┘ │
│ │
│ 移动端支持: │
│ ┌───────────┐ ┌───────────┐ │
│ │ React │ │ React │ │
│ │ Native │ │ Native │ │
│ └───────────┘ └───────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
4. 轻量级 #
text
┌─────────────────────────────────────────────────────────────┐
│ 轻量级设计 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 包大小对比: │
│ │
│ tRPC 核心: ~10KB (gzip) │
│ GraphQL: ~50KB+ (需要 Apollo 等客户端) │
│ REST + Axios: ~15KB │
│ │
│ 依赖关系: │
│ │
│ tRPC: │
│ - @trpc/server │
│ - @trpc/client │
│ - 无其他必需依赖 │
│ │
│ 优势: │
│ ✅ 最小化依赖 │
│ ✅ 快速安装 │
│ ✅ 小包体积 │
│ ✅ 无运行时开销 │
│ │
└─────────────────────────────────────────────────────────────┘
tRPC vs 其他方案 #
tRPC vs REST #
| 特性 | tRPC | REST |
|---|---|---|
| 类型安全 | ✅ 端到端 | ❌ 需要额外工具 |
| Schema | ✅ 零 Schema | ⚠️ 需要 OpenAPI |
| 自动补全 | ✅ 完整 | ❌ 无 |
| 学习曲线 | ✅ 低 | ✅ 低 |
| 通用性 | ⚠️ TypeScript 限定 | ✅ 语言无关 |
| 缓存 | ⚠️ 需要处理 | ✅ HTTP 缓存 |
tRPC vs GraphQL #
| 特性 | tRPC | GraphQL |
|---|---|---|
| 类型安全 | ✅ 原生 TypeScript | ✅ Schema 定义 |
| Schema | ✅ 零 Schema | ⚠️ 需要定义 |
| 学习曲线 | ✅ 低 | ⚠️ 中等 |
| 包大小 | ✅ 小 | ⚠️ 较大 |
| 灵活性 | ⚠️ 固定查询 | ✅ 灵活查询 |
| 生态 | 🔄 发展中 | ✅ 成熟 |
tRPC vs gRPC #
| 特性 | tRPC | gRPC |
|---|---|---|
| 语言支持 | ⚠️ TypeScript | ✅ 多语言 |
| 类型安全 | ✅ TypeScript | ✅ Protobuf |
| 学习曲线 | ✅ 低 | ⚠️ 高 |
| 性能 | ✅ 好 | ✅ 极好 |
| 浏览器支持 | ✅ 原生 | ⚠️ 需要 gRPC-Web |
| 开发体验 | ✅ 极佳 | ⚠️ 一般 |
tRPC 的应用场景 #
1. Next.js 全栈应用 #
text
┌─────────────────────────────────────────────────────────────┐
│ Next.js + tRPC │
├─────────────────────────────────────────────────────────────┤
│ │
│ 项目结构: │
│ │
│ src/ │
│ ├── pages/ │
│ │ ├── api/ │
│ │ │ └── trpc/[trpc].ts # tRPC API 路由 │
│ │ ├── _app.tsx # 配置 tRPC Provider │
│ │ └── index.tsx # 页面组件 │
│ │ │
│ ├── server/ │
│ │ ├── routers/ # tRPC 路由定义 │
│ │ │ ├── user.ts │
│ │ │ └── post.ts │
│ │ └── trpc.ts # tRPC 配置 │
│ │ │
│ └── utils/ │
│ └── trpc.ts # 客户端 tRPC 配置 │
│ │
│ 优势: │
│ ✅ 无需额外 API 服务器 │
│ ✅ SSR/SSG 支持 │
│ ✅ 开发体验极佳 │
│ │
└─────────────────────────────────────────────────────────────┘
2. 独立前后端项目 #
text
┌─────────────────────────────────────────────────────────────┐
│ 独立前后端架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 前端项目 │ │ 后端项目 │ │
│ │ (React/Vue) │ │ (Node.js) │ │
│ │ │ HTTP │ │ │
│ │ @trpc/client │ <─────> │ @trpc/server │ │
│ │ │ │ │ │
│ │ 共享类型包 │ <─────> │ 共享类型包 │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 共享类型包: │
│ packages/ │
│ └── shared/ │
│ ├── package.json │
│ └── src/ │
│ └── types.ts # 共享类型定义 │
│ │
└─────────────────────────────────────────────────────────────┘
3. 微服务通信 #
text
┌─────────────────────────────────────────────────────────────┐
│ 微服务架构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ API │ │ User │ │ Order │ │
│ │ Gateway │────>│ Service │ │ Service │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────┐ │
│ │ tRPC 内部通信 │ │
│ │ TypeScript 类型安全调用 │ │
│ └─────────────────────────────────────────────┘ │
│ │
│ 优势: │
│ ✅ 服务间类型安全 │
│ ✅ 统一的调用方式 │
│ ✅ 简化服务发现 │
│ │
└─────────────────────────────────────────────────────────────┘
4. 移动端应用 #
text
┌─────────────────────────────────────────────────────────────┐
│ 移动端应用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ React Native 项目: │
│ │
│ App.tsx │
│ ├── 使用 @trpc/client │
│ ├── 完整类型安全 │
│ └── 与 Web 共享类型 │
│ │
│ 示例: │
│ const { data } = trpc.user.getById.useQuery({ id: 1 }); │
│ │
│ 优势: │
│ ✅ 与 Web 共享后端 │
│ ✅ 类型安全 API 调用 │
│ ✅ 一致的开发体验 │
│ │
└─────────────────────────────────────────────────────────────┘
tRPC 的核心概念 #
Router(路由器) #
typescript
import { t } from './trpc';
const userRouter = t.router({
getById: t.procedure.query(() => { /* ... */ }),
create: t.procedure.mutation(() => { /* ... */ }),
});
const appRouter = t.router({
user: userRouter,
post: postRouter,
});
Procedure(过程) #
typescript
const router = t.router({
query: t.procedure
.input(z.object({ id: z.number() }))
.query(({ input }) => {
return { id: input.id, name: 'John' };
}),
mutation: t.procedure
.input(z.object({ name: z.string() }))
.mutation(({ input }) => {
return { success: true };
}),
subscription: t.procedure
.subscription(() => {
return observable((observer) => {
observer.next({ data: 'update' });
});
}),
});
Context(上下文) #
typescript
interface Context {
user?: User;
req: Request;
}
const t = initTRPC.context<Context>().create();
const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
if (!ctx.user) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return next();
});
tRPC 的优势 #
text
✅ 极致开发体验
- 自动补全
- 类型推断
- 即时反馈
✅ 类型安全
- 端到端类型
- 编译时检查
- 重构友好
✅ 零配置
- 无需 Schema
- 无需代码生成
- 开箱即用
✅ 灵活架构
- 框架无关
- 多种部署方式
- 可扩展设计
✅ 高性能
- 轻量级
- 无运行时开销
- 按需加载
tRPC 的局限性 #
text
⚠️ TypeScript 限定
- 必须使用 TypeScript
- 前后端都需要 TypeScript
- 不适合多语言项目
⚠️ 紧耦合
- 前后端需要共享类型
- 需要共享代码包
- 版本同步要求
⚠️ 公开 API
- 不适合公开 API
- 需要额外处理文档
- 第三方集成困难
⚠️ 学习曲线
- 需要理解 tRPC 概念
- 需要熟悉 Zod 等库
- 调试方式不同
学习路径 #
text
入门阶段
├── tRPC 简介(本文)
├── 快速开始
└── 路由器与过程
进阶阶段
├── 客户端使用
├── 类型系统
├── 中间件
└── 错误处理
高级阶段
├── 高级特性
├── 性能优化
├── 测试策略
└── 生产部署
实战阶段
├── Next.js 集成
├── React 项目实战
├── Vue 项目实战
└── 最佳实践
适用场景判断 #
text
┌─────────────────────────────────────────────────────────────┐
│ 选择指南 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 推荐使用 tRPC: │
│ ✅ TypeScript 全栈项目 │
│ ✅ Next.js 应用 │
│ ✅ 内部 API / 私有项目 │
│ ✅ 快速原型开发 │
│ ✅ 小型到中型团队 │
│ │
│ 不推荐使用 tRPC: │
│ ❌ 需要公开 API │
│ ❌ 多语言项目 │
│ ❌ 非 TypeScript 项目 │
│ ❌ 需要复杂查询(考虑 GraphQL) │
│ ❌ 需要严格版本控制 │
│ │
└─────────────────────────────────────────────────────────────┘
下一步 #
现在你已经了解了 tRPC 的基本概念,接下来学习 快速开始,开始构建你的第一个 tRPC 应用!
最后更新:2026-03-29