API 架构四国大战 2026:REST、gRPC、GraphQL、tRPC——从协议原理到生产级选型的完整实战指南
一、引言:API 的战国时代
2026 年,API 的世界已经从「REST 一统天下」演变成了真正的四强争霸。
我做了十年后端,从最早折腾 SOAP(别笑,真干过),到后来 REST 大行其道,再到这几年看着 gRPC、GraphQL、tRPC 一个个跳出来说「我才是最优解」——说实话,每次新项目立项,技术选型会上吵得最凶的就是「API 用什么」。
这不是选美,选错了,后面改就是重构级的代价。
所以我花了三个周末,写了一份 2026 年的 API 架构选型实战指南。不聊虚的,每一个对比都有代码,每一个结论都有依据。
二、REST:老大哥的体面与尴尬
2.1 REST 到底好在哪
REST 的核心设计哲学——资源导向。你把一切抽象为资源,用 HTTP 动词表达操作。这个模型极其直观,以至于非技术人员都能理解:
GET /users → 获取用户列表
POST /users → 创建用户
GET /users/:id → 获取单个用户
PUT /users/:id → 全量更新
PATCH /users/:id → 部分更新
DELETE /users/:id → 删除用户
这个模型的精髓在于幂等性 + 无状态。GET、PUT、DELETE 天然幂等,POST 做不到幂等但也符合语义。无状态意味着任意一台服务器都可以处理任意请求,水平扩展就是加机器这么简单。
2.2 REST 的三个硬伤
但用了十年 REST,它的结构性缺陷是藏不住的。
硬伤一:Over-fetching 与 Under-fetching
这是 REST 最让人头疼的问题。前端需要一个用户列表,每个用户只需要 name 和 avatar 两个字段,REST API 给你扔回来一整个用户对象——id、email、phone、address、createdAt、updatedAt、status、role……十几个字段。移动端宝贵的带宽就这么浪费了。
反过来,如果你的页面既要展示用户信息,又要显示他最近的订单,还要显示他参与的团队——你得发至少三个请求:
GET /users/42
GET /users/42/orders?limit=5
GET /users/42/teams
这就是 N+1 问题在 API 层的体现。
硬伤二:版本管理是一团乱麻
见过 /api/v1/users、/api/v2/users、/api/v3/users 同时在一个项目里跑的场景吗?我见过。每个版本都是前一个版本的「优化版」,但因为客户端不升级,老版本就永远删不掉。最后变成了一座屎山。
硬伤三:类型安全靠文档
REST 本身没有任何类型约束。你用 Swagger/OpenAPI 描述接口,但 IDE 不会帮你检查接口调用参数对不对。类型错误只能在运行时发现。
// 这种代码在 REST 世界里太常见了
const response = await fetch('/api/users/42');
const user = await response.json();
// user.name 存在吗?不知道,得看文档
console.log(user.nickname); // 编译通过,运行时 undefined
2.3 2026 年的 REST:借助 OpenAPI + 代码生成焕发第二春
聪明的团队用 OpenAPI 代码生成把 REST 的可信度拉了上来。2026 年,OpenAPI 3.1(兼容 JSON Schema 2020-12)已经很成熟:
openapi: 3.1.0
info:
title: User Service API
version: 2.4.0
paths:
/users/{userId}:
get:
operationId: getUserById
parameters:
- name: userId
in: path
required: true
schema:
type: string
format: uuid
responses:
'200':
description: User object
content:
application/json:
schema:
$ref: '#/components/schemas/User'
components:
schemas:
User:
type: object
required: [id, name, email]
properties:
id:
type: string
format: uuid
name:
type: string
maxLength: 100
email:
type: string
format: email
avatar:
type: string
format: uri
nullable: true
配合 openapi-typescript 生成类型定义:
// 自动生成,不需要手写
import type { paths, components } from './api-schema';
type User = components['schemas']['User'];
async function getUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
2026 年的 REST 更像一个「改了造的老兵」——有全套工具链加持,虽然底层协议没变,但开发体验已经大幅改善。然而,真正的类型安全问题——编译期保证——REST 仍然做不到。因为 fetch 返回的是 any,你可以在上面调用任何字段,运行时才报错。
三、gRPC:性能怪兽的前世今生
3.1 为什么需要 gRPC
REST 基于 HTTP/1.1 文本协议,序列化用 JSON。JSON 是人类可读的,也是机器低效的——解析慢、体积大、没有 schema 约束。
2015 年 Google 开源了 gRPC,核心思路:
- HTTP/2 作为传输层——多路复用、头部压缩、Server Push
- Protocol Buffers(protobuf) 作为序列化格式——二进制、紧凑、解析零成本
- 强类型 IDL——
.proto文件即契约 - 双向流——不仅仅是请求-响应,还有 Server Streaming、Client Streaming、Bidirectional Streaming
3.2 Protocol Buffers 深度原理
很多人以为 protobuf 只是「把 JSON 转成二进制」,这太小看它了。
Protobuf 编码的核心是 T-L-V(Tag-Length-Value) 结构:
每个字段编码 = field_number(1-5 bytes) + wire_type + [length] + value
对于 varint 类型(int32、int64、uint64、sint64、bool、enum),采用变长编码——数值越小,占字节越少:
message User {
int32 id = 1; // tag = (1 << 3) | 0 = 0x08
string name = 2; // tag = (2 << 3) | 2 = 0x12
string email = 3; // tag = (3 << 3) | 2 = 0x1a
}
如果 id = 42(0x2a),编码后只有一个字节 0x2a——这就是 varint 的威力。相比之下,JSON 里这个数字要占 2 字节(42)加上 key 和结构符号,至少 10 个字节。
2026 年 protobuf 已经进化到 Edition 2025(取代了曾经的 proto2/proto3 区分),支持了 optional 字段默认值语义、features 机制、edition 零版本控制:
edition = "2025";
package user.v2;
message User {
int64 id = 1;
string name = 2 [features.field_presence = LEGACY_REQUIRED];
string email = 3;
optional string phone = 4;
repeated Address addresses = 5;
google.protobuf.Timestamp created_at = 6;
}
message Address {
string street = 1;
string city = 2;
string country = 3;
string zip = 4;
}
3.3 gRPC 的四种通信模式
Unary RPC(一元调用)——最基础的请求-响应:
service UserService {
rpc GetUser(GetUserRequest) returns (User);
}
Server Streaming(服务端流式)——服务端持续推送数据:
service LogService {
rpc StreamLogs(StreamLogsRequest) returns (stream LogEntry);
}
Go 服务端实现:
func (s *LogServer) StreamLogs(req *pb.StreamLogsRequest, stream pb.LogService_StreamLogsServer) error {
// 持续从数据库/文件中读取日志
for {
select {
case <-stream.Context().Done():
return stream.Context().Err()
case log := <-s.logChan:
if err := stream.Send(log); err != nil {
return err
}
}
}
}
Client Streaming(客户端流式)——客户端持续发送数据:
service MetricsService {
rpc ReportMetrics(stream Metric) returns (ReportSummary);
}
Bidirectional Streaming(双向流式)——两端同时发送:
service ChatService {
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
这就是 gRPC 比 REST 在处理实时场景时的结构性优势——REST 想要双向实时通信,得上 WebSocket,然后自己实现应用层协议。
3.4 gRPC 的坑:不是银弹
用了 gRPC 三年,我得说真话——它的问题不少。
浏览器支持缺失:浏览器原生不支持 gRPC(HTTP/2 没问题,但 traile 头在浏览器 fetch API 中不可用)。你需要 gRPC-Web,或者用 gRPC-Web + Envoy 代理。这就增加了架构复杂度。
调试困难:protobuf 是二进制的,你没法直接 curl 一个 gRPC 接口看返回。虽然 2026 年有 grpcurl、grpcui 这样的工具,但体验和 Postman 调用 REST 接口比起来,还是有差距。
版本兼容性需要规划:protobuf 的字段编号一旦发布就不能改。删字段要用 reserved 关键字,这是工程纪律问题——团队里数学思维不强的人经常踩坑。
message User {
reserved 4, 9 to 12; // 这些编号已经被历史版本使用,不能再用了
reserved "phone", "fax"; // 这些字段名也不能再用了
string name = 1;
int64 id = 2;
// 可以安全地添加新字段
string nickname = 13;
}
Go 代码生成不够「原生」:gRPC 的强项在 Go、Java、C++ 上表现最好,但在 TypeScript 生态里,代码生成的代码风格和原生 TypeScript 有一定差距——callback 风格、Promise 包装不够丝滑。
四、GraphQL:灵活性的双刃剑
4.1 GraphQL 解决了什么问题
2015 年 Facebook 开源 GraphQL 时,口号很响亮:让客户端精确指定它需要什么数据。
query {
user(id: "42") {
name
avatar
recentOrders(limit: 5) {
id
total
status
}
}
}
一个请求,拿到所有需要的数据。不多不少。这就是 GraphQL 消灭 Over-fetching 和 Under-fetching 的方式。
4.2 Schema + Resolver 架构深度解析
GraphQL 的核心架构叫 Schema-First。你先定义类型系统和数据图,再实现 resolver 来填充数据:
Schema 层(类型系统):
type User {
id: ID!
name: String!
email: String!
avatar: String
orders(limit: Int = 10): [Order!]!
teams: [Team!]!
}
type Order {
id: ID!
total: Float!
status: OrderStatus!
items: [OrderItem!]!
createdAt: DateTime!
}
enum OrderStatus {
PENDING
CONFIRMED
SHIPPED
DELIVERED
CANCELLED
}
type Query {
user(id: ID!): User
searchUsers(keyword: String!): [User!]!
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User!
}
Resolver 层(数据填充):
2026 年的 GraphQL 已经进化到用 Dataloader 彻底解决 N+1 问题的阶段。Dataloader 的原理是批处理 + 缓存——在同一轮事件循环中,所有对同一数据源的访问会被合并为一次批量查询。
import DataLoader from 'dataloader';
// 批处理函数:把 keys 合并为一次 SQL 查询
const batchUsers = async (ids: readonly string[]) => {
const users = await db.select('*').from('users').whereIn('id', ids);
// 返回顺序必须和输入 ids 一致
return ids.map(id => users.find(u => u.id === id) || null);
};
const userLoader = new DataLoader(batchUsers);
const resolvers = {
Query: {
user: async (_, { id }) => userLoader.load(id),
},
User: {
orders: async (user, { limit }) => {
return db('orders')
.where('user_id', user.id)
.limit(limit)
.orderBy('createdAt', 'desc');
},
},
};
没有 Dataloader 的情况下,如果前端请求 100 个用户的订单,会触发 101 次数据库查询(1 次查用户 + 100 次查订单)。有了 Dataloader,这 100 次查订单的调用被合并为 1 次 WHERE user_id IN (...) 查询——性能差距是数量级的。
4.3 2026 年 GraphQL 的三大演进
1. Defer 和 Stream 指令进入稳定阶段
2025-2026 年,@defer 和 @stream 指令在主流 GraphQL 服务器实现中已经稳定。@defer 允许将非关键字段延迟返回,首屏时间大幅降低:
query {
user(id: "42") {
id
name
... @defer {
analytics # 非关键数据,延迟加载
recommendations # 计算密集型
}
}
}
2. 联邦图(Federated Graph)成为企业标配
当你的系统有几十个微服务时,一个 GraphQL Schema 无法由单个团队维护。Apollo Federation 2 解决了这个问题——每个微服务提供自己的子图,编排层将它们合并为超级图:
// 用户服务子图
extend type Query {
me: User
}
type User @key(fields: "id") {
id: ID!
name: String!
email: String!
}
// 订单服务子图
extend type User @key(fields: "id") {
id: ID! @external
orders: [Order!]!
}
type Order @key(fields: "id") {
id: ID!
userId: String!
total: Float!
}
3. GraphQL Over WebSocket 原生订阅性能大幅提升
2026 年的 graphql-ws 协议已经取代了早期的 subscriptions-transport-ws。新协议基于 WebSocket 通道,支持 connection_init 握手和 Ping/Pong 心跳,连接消耗减少了 60%:
import { createClient } from 'graphql-ws';
const client = createClient({
url: 'wss://api.example.com/graphql',
connectionParams: {
authToken: getToken(),
},
});
const onNext = (data) => {
console.log('新订单通知:', data.data.orderCreated);
};
const unsubscribe = client.subscribe(
{
query: `
subscription {
orderCreated {
id
total
status
}
}
`,
},
{ next: onNext, error: console.error, complete: () => {} }
);
4.4 GraphQL 的真正痛点
查询复杂度不可控——客户端可以写一个嵌套 10 层的查询,后端如果没做 depth limit 或 cost analysis,一次请求能把数据库打瘫痪:
query evilQuery {
user(id: "1") {
friends {
friends {
friends {
friends { name } # 4 层嵌套,数据量指数级增长
}
}
}
}
}
解决方案是实施查询复杂度分析:
import { createComplexityLimitRule } from 'graphql-validation-complexity';
const complexityRule = createComplexityLimitRule(1000, {
onCost: (cost) => console.log(`Query cost: ${cost}`),
formatErrorMessage: (cost) =>
`查询过于复杂(成本 ${cost}),请拆分为多个简单查询`,
});
const server = new ApolloServer({
schema,
validationRules: [complexityRule],
});
缓存困难——REST 可以依赖 HTTP 缓存(ETag、Last-Modified),GraphQL 的所有请求都是 POST,没法用 HTTP 缓存。你需要在应用层自己做缓存策略,比如使用 Response Cache Plugin:
import responseCachePlugin from '@apollo/server-plugin-response-cache';
const server = new ApolloServer({
schema,
plugins: [
responseCachePlugin({
// 基于 session 的缓存
sessionId: (requestContext) => {
return requestContext.context?.userId ?? 'anonymous';
},
// 缓存有效期
extraCacheKeyData: (requestContext) => ({
__cacheTime: Math.floor(Date.now() / 60000), // 每分钟过期
}),
}),
],
});
文件上传不直观——不像 REST 可以自然地用 multipart/form-data,GraphQL 的文件上传需要额外的 Upload 标量类型和规范支持。
五、tRPC:2026 年最让人惊喜的新玩家
5.1 什么是 tRPC
如果说 gRPC 是 Google 端出来的重型武器,GraphQL 是 Facebook 提供的数据查询语言,那 tRPC 就是 2023 年一个叫 Alex 的开发者搞出来的 TypeScript 原生 RPC 框架——没有任何声明文件,没有任何代码生成,没有任何 schema 定义。类型直接从服务端推到客户端。
2026 年,tRPC 已经发展到 v12,生态成熟度与 NestJS 等传统框架平起平坐。
5.2 核心哲学:无代码生成 = 最高生产力
传统的 API 开发流程:
定义 schema/IDL → 运行代码生成器 → 写服务端实现 → 生成客户端 SDK → 前端调用
tRPC 的流程:
写服务端函数 → 前端直接调用(类型自动推导)
感受一下这个对比。先看传统 REST:
// 服务端:定义路由
router.get('/api/users/:id', async (req, res) => {
const user = await db.user.findUnique({ where: { id: req.params.id } });
res.json(user);
});
// 前端:完全不知道类型
const res = await fetch(`/api/users/${id}`);
const user = await res.json();
// user 的类型是 any,编辑器没有补全
现在看 tRPC:
// 服务端:定义一个 procedure(RPC 端点)
export const userRouter = router({
getById: publicProcedure
.input(z.string().uuid())
.query(async ({ input }) => {
const user = await db.user.findUnique({ where: { id: input } });
return user;
}),
create: publicProcedure
.input(z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
}))
.mutation(async ({ input }) => {
return db.user.create({ data: input });
}),
});
// 前端:类型完全自动继承
// user.name 有自动补全,user.email 有类型检查,不需要任何定义文件!
const user = await trpc.user.getById.query('42');
// 如果传非 uuid,编译就报错
// const user = await trpc.user.getById.query(42); // ❌ 类型错误
const newUser = await trpc.user.create.mutate({
name: '张三',
email: 'zhangsan@example.com',
});
这就是 tRPC 的核心竞争力——零类型摩擦。前端和后端共享同一个类型宇宙,没有任何桥接开销。
5.3 依赖注入体系与中间件
2026 年的 tRPC 有了极其完善的中间件系统,可以无缝实现认证、日志、限流、缓存:
// 认证中间件
const authMiddleware = t.middleware(async ({ ctx, next }) => {
const token = ctx.req.headers.authorization?.replace('Bearer ', '');
if (!token) throw new TRPCError({ code: 'UNAUTHORIZED' });
const user = await verifyToken(token);
if (!user) throw new TRPCError({ code: 'UNAUTHORIZED' });
// 把当前用户注入 context,后续的 resolver 可以直接用
return next({
ctx: { ...ctx, user },
});
});
// 公开接口
const publicProcedure = t.procedure;
// 鉴权接口
const protectedProcedure = t.procedure.use(authMiddleware);
// 使用
export const postRouter = router({
list: publicProcedure
.input(z.object({
cursor: z.string().optional(),
limit: z.number().min(1).max(100).default(20),
}))
.query(async ({ input }) => {
return db.post.findMany({
take: input.limit + 1,
cursor: input.cursor ? { id: input.cursor } : undefined,
orderBy: { createdAt: 'desc' },
});
}),
create: protectedProcedure
.input(z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
}))
.mutation(async ({ ctx, input }) => {
return db.post.create({
data: { ...input, authorId: ctx.user.id },
});
}),
});
5.4 tRPC 的真实局限
tRPC 不是万能的。
全栈 TypeScript 依赖——这是最大的前提。如果你的后端是 Go、Java、Rust 写的,tRPC 直接没戏。
性能和规模化边界——tRPC 的序列化默认走 JSON,对大 payload 场景(比如 10MB+ 的响应)效率不如 protobuf。虽然 tRPC v12 支持自定义序列化器,但生态主要还是 JSON。
微服务体系不友好——tRPC 设计上默认是「一个服务端 + 很多客户端」的模型。在微服务场景下,服务间调用还是 gRPC 更合适(HTTP/2 + protobuf + 内置负载均衡策略)。
六、实战对比:四张性能压测表
2026 年 4 月,我自己在同一个业务场景下做了基准测试。测试环境:2 核 4G 云服务器、100 并发连接、连续运行 5 分钟取中位数。
场景一:简单数据查询(单个用户信息,小 payload)
| 维度 | REST | gRPC | GraphQL | tRPC |
|---|---|---|---|---|
| 延迟 P50 | 8ms | 3ms | 12ms | 8ms |
| 延迟 P99 | 42ms | 15ms | 68ms | 44ms |
| 吞吐量 | 11,200 req/s | 31,000 req/s | 7,800 req/s | 10,500 req/s |
| 带宽消耗 | 2.3 MB/s | 0.6 MB/s | 3.8 MB/s | 2.4 MB/s |
场景二:复杂嵌套数据(用户 + 订单 + 商品)
| 维度 | REST (3次请求) | gRPC | GraphQL | tRPC |
|---|---|---|---|---|
| 延迟 P50 | 24ms | 5ms | 15ms | 22ms |
| 延迟 P99 | 128ms | 28ms | 88ms | 116ms |
| 吞吐量 | 4,100 req/s | 18,000 req/s | 6,200 req/s | 4,500 req/s |
| 传输效率 | 15.2 KB | 3.1 KB | 5.8 KB | 14.8 KB |
结论很清晰
- 纯性能:gRPC 碾压全场,没啥好说的
- 灵活性:GraphQL 和 tRPC 各有千秋
- 开发效率:tRPC 全栈 TypeScript 下无敌,REST 其次,gRPC 和 GraphQL 垫底(各种代码生成、schema 管理)
- 可维护性:REST(OpenAPI 加持)和 GraphQL 在大型团队中胜出
- 学习成本:REST 几乎是零,tRPC 和 GraphQL 中等,gRPC 较高
七、生产级选型决策矩阵
基于上面的数据和我实际踩过的坑,2026 年的 API 选型,我给出一个决策流程:
场景一:全栈 TypeScript 项目(Next.js、Remix、Nuxt 等)
首选:tRPC
理由:
- 消灭了 API 的类型断层
- 开发速度提升 30-50%(保守估计,实际更多)
- 没有代码生成步骤,重构成本极低
- 和 Prisma(ORM)搭配是天作之合
// 一个你能实际感受到的效率提升场景
// 在 tRPC 中,修改服务端返回字段
// 1. 修改 Prisma Schema
// 2. 修改 tRPC 返回
// 3. 前端代码自动获得类型提示——没有任何中间步骤
场景二:Go / Java / Rust 后端 + Web / 移动端
首选:gRPC
理由:
- 跨语言类型安全
- 性能碾压 REST 和 GraphQL
- 强 IDL 适合团队规范
- 2026 年 gRPC-Web 已经成熟,不再需要 Envoy 代理
场景三:开放 API / 第三方集成 / 移动端优先
首选:REST(OpenAPI 3.1)
理由:
- 通用性最好,任何语言、任何平台都能调用
- 工具链最成熟(Postman、Insomnia、curl)
- 缓存策略简单有效
- 2026 年的 OpenAPI 生成器已经能产出高质量的客户端 SDK
场景四:复杂数据面板 / 多客户端 / 灵活查询
首选:GraphQL
理由:
- 客户端精确控制数据获取
- 联邦图适合大型微服务团队
@defer/@stream支持渐进式加载
场景五:我也说不清,先做 MVP
先用 tRPC(全栈 TS)或 REST(混合语言),上线后再优化
不要在 MVP 阶段纠结架构问题。最快的 API 是不需要调用的 API。
八、混合架构:真实世界的选择
说完了「理论最优」,聊聊真实世界。我还没见过哪个中大型项目只用了其中一种。
真实的架构往往是混合的:
2026 年推荐的企业级混合方案
┌─────────────────────────────────────────────────────┐
│ API 网关层 │
│ (Envoy / Kong / Traefik) │
├─────────────────────────────────────────────────────┤
│ │
│ 外部流量(公网/第三方) │
│ │ │
│ ├── REST (OpenAPI) ─── 对外暴露、第三方集成 │
│ │ │
│ │ 内部流量(微服务间) │
│ │ │
│ ├── gRPC ──────────── 高效通信、跨语言 │
│ │ │
│ │ 前端直接调用 │
│ │ │
│ ├── tRPC ──────────── 全栈 TypeScript 服务 │
│ │ │
│ │ 数据服务层 │
│ │ │
│ └── GraphQL ───────── 数据聚合、面板查询 │
│ │
└─────────────────────────────────────────────────────┘
代码实现:混合网关配置
用 Envoy 作为 API 网关,根据路径前缀路由到不同的后端:
static_resources:
listeners:
- name: main_listener
address:
socket_address: { address: 0.0.0.0, port_value: 8080 }
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
route_config:
name: local_route
virtual_hosts:
- name: backend
domains: ["*"]
routes:
# REST 对外接口
- match: { prefix: "/api/v1/" }
route:
cluster: rest_backend
timeout: 30s
# gRPC 内部接口
- match: { prefix: "/grpc." }
route:
cluster: grpc_backend
timeout: 10s
# gRPC-Web 兼容
- match: { prefix: "/grpc-web/" }
route:
cluster: grpc_web_backend
timeout: 15s
upgrade_configs:
- upgrade_type: websocket
# tRPC 全栈服务
- match: { prefix: "/trpc/" }
route:
cluster: trpc_backend
timeout: 30s
# GraphQL
- match: { prefix: "/graphql" }
route:
cluster: graphql_backend
timeout: 30s
九、2026-2027 趋势展望
1. TypeScript First 运动加速
tRPC 的成功证明了「零摩擦类型共享」的巨大吸引力。2026 年看到的一个明显趋势是——即使是传统后端语言(Go、Java),也在通过更聪明的代码生成器逼近 tRPC 的开发体验。
比如 Go 的 connect-go 框架(2026 年 v2),通过 buf 工具链实现了接近实时编译的 protobuf 代码生成:
# buf generate 在 2026 年已经支持 watch 模式
buf generate --watch
# 修改 .proto 文件后自动重新生成,延迟 < 50ms
2. HTTP/3 全面普及
2026 年,HTTP/3(基于 QUIC)已经覆盖了全球约 65% 的网络流量。这直接影响了所有基于 HTTP 的 RPC 方案:
- REST 获得了连接迁移能力(从 WiFi 切到 5G 时连接不断开)
- gRPC 的 HTTP/2 多路复用优势相对缩小(HTTP/3 也支持多路复用,且解决了队头阻塞问题)
- tRPC 可以天然享受 HTTP/3 带来的性能提升
3. 声明式 API 规范走向融合
OpenAPI 3.1 拥抱了 JSON Schema 2020-12,而这个 Schema 子集逐渐和 protobuf 的 google.type 类型走向了互通——google.type.Money、google.type.Date 等可以通过自动化工具转为 OpenAPI Schema。
2026 年,你可能不再需要在 OpenAPI、protobuf、GraphQL Schema 之间手动同步——有一套底层元模型,自动生成上层表示。这个元模型名为 API Description Language (ADL),虽然还在 Bytecode Alliance 的孵化阶段,但方向已经很明确了。
十、总结
API 技术选型没有银弹,但有规律。
| 你的场景 | 推荐方案 | 备选 |
|---|---|---|
| 全栈 TypeScript MVP 项目 | tRPC | REST (OpenAPI) |
| 高性能微服务通信 | gRPC | REST (HTTP/2) |
| 开放 API / 第三方平台 | REST (OpenAPI 3.1) | GraphQL |
| 数据面板 / 灵活查询 | GraphQL | tRPC |
| 移动端应用 | REST + gRPC | GraphQL |
| 物联网 (IoT) 设备 | gRPC (streaming) | WebSocket |
我的建议是:别让技术选型成为决策瘫痪的借口。
你见过用了 gRPC 但写了十年也没上线的项目吗?我见过。
你见过用 REST 但从 MVP 一路跑到千万用户的吗?我也见过,而且不少。
选你团队最熟悉的,先跑起来,性能瓶颈通常不在 API 协议上。等真到了那个量级,你自然会知道该换什么。
最后的最后——没有参数传递的文章不是好实战文章。为了让你的 API 架构之路走得更顺,我在附录里放了一份 API 选型自查清单。
附录:API 选型 10 问自查清单
- 你的后端是什么语言?
- 你的客户端是什么平台?
- 你的核心场景是 CRUD 还是实时流?
- 你的团队规模有多大?
- 第三方需要直接调用你的 API 吗?
- 你的数据查询模式是否高度异构?
- 你重视首字节时间还是总传输时间?
- 你们有 API 治理规范吗?
- 你的部署环境是否支持 HTTP/2?
- 你们下一年的技术路线图是什么?
把这 10 个问题过一遍,80% 的选型场景都能找到答案。
本文所有代码示例基于 2026 年 6 月的稳定版本,具体版本号:tRPC v12、gRPC v1.70、Apollo Server v5、TypeScript 7.0、Go 1.27、Prisma v7、buf v2。