GraphQL vs REST API:2026年API设计选型指南

别再问“谁更好”,先问“场景是什么”

REST 和 GraphQL 之争在网上吵了十年,但成熟的工程师都知道:技术选型从来不是非黑即白。REST 是资源导向的 HTTP 接口约定,GraphQL 是一套查询语言 + 运行时。它们解决的核心问题都是“前后端如何交换数据”,但代价和收益截然不同。

本文从多个工程维度做一次冷静对比,并给出 2026 年的选型建议。

数据获取:过取与欠取

REST 最大的痛点是“端点固定、返回固定”。一个文章详情页可能需要:文章本体、作者信息、评论列表。REST 常见做法是三个端点,前端发三次请求;或者后端专门做一个聚合端点 /articles/{id}/full,结果移动端只需要标题,又取回了一堆冗余字段。

  • REST 的过取/欠取问题:端点返回字段固定,要么取多了浪费流量,要么取少了得再发请求。

  • GraphQL 的精准获取:前端声明需要哪些字段,后端只返回这些字段。
  • graphql
    # 前端精确声明需要的字段
    query ArticleDetail($id: ID!) {
      article(id: $id) {
        title
        author {
          name
          avatar
        }
        comments(last: 5) {
          content
          createdAt
        }
      }
    }

    一次请求拿到所有数据,且没有多余字段。这是 GraphQL 最具说服力的优势。

    REST 依然不可替代的地方

    但 REST 并非没有还手之力,它在以下场景仍然更优:

  • 缓存。REST 天然贴合 HTTP 缓存语义,ETagCache-Control、CDN 缓存开箱即用。GraphQL 默认是单个 POST 端点,缓存要靠 Apollo Clientnormalized cachePersisted Query,复杂度高得多。

  • 文件上传/流式响应。大文件、SSE、WebSocket 这些场景 REST 更直接,GraphQL 处理二进制流并不优雅。

  • 简单 CRUD。一个内部管理后台的增删改查,REST 几分钟搞定,GraphQL 反而是杀鸡用牛刀。

  • 可观测性与网关治理。REST 端点粒度清晰,API 网关按路径限流、计费、鉴权都很自然。GraphQL 单端点让这些治理变复杂。
  • 版本管理

    REST 的版本管理是个老大难:/v1/users 还是 /v2/users?多个版本长期共存让维护成本飙升。

    GraphQL 通过 Schema 演进优雅地解决这个问题:新增字段不影响旧客户端;废弃字段标记 @deprecated,客户端有充裕时间迁移;真正要破坏性变更时才考虑版本。

    graphql
    type User {
      id: ID!
      name: String!
      fullName: String @deprecated(reason: "Use `name` instead.")
    }

    类型系统与前端体验

    GraphQL 的 Schema 就是强类型契约。配合代码生成(如 graphql-codegen),前端拿到的请求函数和类型是完全同步的,TS 项目体验极佳。

    typescript
    // codegen 自动生成
    export type ArticleQuery = {
      article: {
        __typename: "Article";
        title: string;
        author: { __typename: "Author"; name: string };
      } | null;
    };

    REST 也能用 OpenAPI 规范 + 代码生成达到类似效果,但需要额外维护 Swagger 文档并保证与代码同步,实践中常常脱节。

    N+1 与性能陷阱

    GraphQL 的灵活性也是它的“坑”。前端可以嵌套查询 user -> posts -> comments -> author,一条查询可能触发数据库 N+1 问题,瞬间拖垮后端。

    解决之道是 DataLoader:在单次请求内批量加载、去重、缓存关联数据。

    javascript
    const { DataLoader } = require("@apollo/server");
    
    const userLoader = new DataLoader(async (userIds) => {
      // 一次性查出所有 user,按顺序返回
      const users = await db.users.findMany({ id: { in: userIds } });
      return userIds.map((id) => users.find((u) => u.id === id));
    });
    
    const resolvers = {
      Comment: {
        author: (comment) => userLoader.load(comment.authorId),
      },
    };

    此外还要给查询设置深度限制复杂度限制,防止恶意嵌套查询打爆服务器。

    javascript
    import depthLimit from "graphql-depth-limit";
    
    const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)],
    });

    鉴权与错误处理


  • REST:每个端点独立鉴权,HTTP 状态码语义清晰(401 未认证、403 无权限、404 不存在)。

  • GraphQL:通常返回 HTTP 200,错误信息放在响应体的 errors 数组里。鉴权在 resolver 层按字段粒度做,更灵活但也更易出错。
  • javascript
    const resolvers = {
      Query: {
        me: (_, __, { user }) => {
          if (!user) throw new GraphQLError("未登录", { extensions: { code: "UNAUTHENTICATED" } });
          return user;
        },
      },
    };

    2026 年的选型建议

    结合实际工程经验,给出如下决策参考:

    | 场景 | 推荐 | 理由 |
    | --- | --- | --- |
    | 移动端为主、流量敏感 | GraphQL | 精准取字段,省流量、省请求 |
    | 多端共用一套 API(Web/App/小程序) | GraphQL | 一处 Schema,多端按需取数 |
    | 对外开放 API、第三方集成 | REST | 标准、可缓存、网关治理简单 |
    | 内部微服务间通信 | REST / gRPC | 简单直接、性能优先 |
    | 复杂聚合、关系型数据展示 | GraphQL | 避免多次往返 |
    | 简单 CRUD 后台 | REST | 上手快、维护成本低 |

    一个越来越常见的折中方案是 BFF(Backend for Frontend)+ GraphQL 网关:底层微服务用 REST/gRPC,面向前端的聚合层用 GraphQL,既保留了微服务的简洁,又给前端提供了灵活查询能力。

    渐进式迁移

    很多团队担心“已有大量 REST 接口,怎么上 GraphQL”。其实可以共存:

  • 在网关层引入 Apollo Server,resolver 内部调用现有 REST 接口(REST DataSource)。

  • 新功能优先用 GraphQL,旧接口保持 REST 不动。

  • 前端逐步迁移,最终下线无人使用的 REST 端点。
  • javascript
    class UsersAPI extends RESTDataSource {
      baseURL = "https://api.internal/v1/";
    
      async getUser(id) {
        return this.get(`users/${id}`);
      }
    }

    小结

    GraphQL 不是 REST 的替代品,而是补充。GraphQL 赢在数据获取灵活性和类型契约,REST 赢在缓存、治理和简单性。选型的核心是看你的前端是否真有“多端、多变、按需取数”的痛点。如果答案是肯定的,GraphQL 会让前端开发体验上一个台阶;如果只是简单的资源 CRUD,REST 依旧是性价比最高的选择。成熟架构里,两者共存、各司其职,才是 2026 年的常态。

    💬 评论区 (0)

    暂无评论,快来抢沙发吧!