编程 API 架构四国大战 2026:REST、gRPC、GraphQL、tRPC —— 从协议原理到生产级选型的完整实战指南

2026-06-25 22:45:20 +0800 CST views 6

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 最让人头疼的问题。前端需要一个用户列表,每个用户只需要 nameavatar 两个字段,REST API 给你扔回来一整个用户对象——idemailphoneaddresscreatedAtupdatedAtstatusrole……十几个字段。移动端宝贵的带宽就这么浪费了。

反过来,如果你的页面既要展示用户信息,又要显示他最近的订单,还要显示他参与的团队——你得发至少三个请求:

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,核心思路:

  1. HTTP/2 作为传输层——多路复用、头部压缩、Server Push
  2. Protocol Buffers(protobuf) 作为序列化格式——二进制、紧凑、解析零成本
  3. 强类型 IDL——.proto 文件即契约
  4. 双向流——不仅仅是请求-响应,还有 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)

维度RESTgRPCGraphQLtRPC
延迟 P508ms3ms12ms8ms
延迟 P9942ms15ms68ms44ms
吞吐量11,200 req/s31,000 req/s7,800 req/s10,500 req/s
带宽消耗2.3 MB/s0.6 MB/s3.8 MB/s2.4 MB/s

场景二:复杂嵌套数据(用户 + 订单 + 商品)

维度REST (3次请求)gRPCGraphQLtRPC
延迟 P5024ms5ms15ms22ms
延迟 P99128ms28ms88ms116ms
吞吐量4,100 req/s18,000 req/s6,200 req/s4,500 req/s
传输效率15.2 KB3.1 KB5.8 KB14.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.Moneygoogle.type.Date 等可以通过自动化工具转为 OpenAPI Schema。

2026 年,你可能不再需要在 OpenAPI、protobuf、GraphQL Schema 之间手动同步——有一套底层元模型,自动生成上层表示。这个元模型名为 API Description Language (ADL),虽然还在 Bytecode Alliance 的孵化阶段,但方向已经很明确了。

十、总结

API 技术选型没有银弹,但有规律。

你的场景推荐方案备选
全栈 TypeScript MVP 项目tRPCREST (OpenAPI)
高性能微服务通信gRPCREST (HTTP/2)
开放 API / 第三方平台REST (OpenAPI 3.1)GraphQL
数据面板 / 灵活查询GraphQLtRPC
移动端应用REST + gRPCGraphQL
物联网 (IoT) 设备gRPC (streaming)WebSocket

我的建议是:别让技术选型成为决策瘫痪的借口。

你见过用了 gRPC 但写了十年也没上线的项目吗?我见过。
你见过用 REST 但从 MVP 一路跑到千万用户的吗?我也见过,而且不少。

选你团队最熟悉的,先跑起来,性能瓶颈通常不在 API 协议上。等真到了那个量级,你自然会知道该换什么。

最后的最后——没有参数传递的文章不是好实战文章。为了让你的 API 架构之路走得更顺,我在附录里放了一份 API 选型自查清单


附录:API 选型 10 问自查清单

  1. 你的后端是什么语言?
  2. 你的客户端是什么平台?
  3. 你的核心场景是 CRUD 还是实时流?
  4. 你的团队规模有多大?
  5. 第三方需要直接调用你的 API 吗?
  6. 你的数据查询模式是否高度异构?
  7. 你重视首字节时间还是总传输时间?
  8. 你们有 API 治理规范吗?
  9. 你的部署环境是否支持 HTTP/2?
  10. 你们下一年的技术路线图是什么?

把这 10 个问题过一遍,80% 的选型场景都能找到答案。


本文所有代码示例基于 2026 年 6 月的稳定版本,具体版本号:tRPC v12、gRPC v1.70、Apollo Server v5、TypeScript 7.0、Go 1.27、Prisma v7、buf v2。

推荐文章

WebSQL数据库:HTML5的非标准伴侣
2024-11-18 22:44:20 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
PHP 微信红包算法
2024-11-17 22:45:34 +0800 CST
robots.txt 的写法及用法
2024-11-19 01:44:21 +0800 CST
使用Vue 3实现无刷新数据加载
2024-11-18 17:48:20 +0800 CST
windows下mysql使用source导入数据
2024-11-17 05:03:50 +0800 CST
PostgreSQL日常运维命令总结分享
2024-11-18 06:58:22 +0800 CST
程序员茄子在线接单