GraphQL — 它是什么,查询语言以及在移动项目中的应用

作者: IT Sectr 发布日期: 2026-03-06 阅读时间: 9 分钟

GraphQL — 是一种用于 API 的查询语言和执行这些查询的运行时环境,由 Facebook 于 2012 年开发,并于 2015 年开源。与 REST 不同,REST 中服务器决定响应的结构,而 GraphQL 允许客户端精确指定需要哪些数据,从而完全消除 overfetching 和 underfetching 的问题。根据 State of JavaScript Survey (2025),35% 的受访开发者使用 GraphQL,在大型公司中,GitHub、Shopify、Airbnb 和 The New York Times 已采用它。GraphQL 支持三种类型的操作:query(读取)、mutation(写入)和 subscription(通过 WebSocket 实现实时更新)。

要点

  • GraphQL — 客户端可以在其中指定响应结构的查询语言
  • 解决 overfetching(过多数据)和 underfetching(数据不足)的问题
  • 支持 query、mutation 和 subscription 用于不同类型的操作
  • 使用单个 endpoint(通常为 /graphql)而不是像 REST 那样的多个 URL
  • 基于具有严格架构的 类型系统:所有可能的数据都提前描述好

什么是 GraphQL?

GraphQL — 是一种用于 API 的规范和运行时环境,让客户端完全控制接收到的数据。由 Facebook 的工程师为解决移动应用 News Feed 的问题而开发,该规范于 2015 年作为开放标准发布。自 2018 年起,GraphQL 由 GraphQL 基金会管理,并得到 Linux 基金会以及 Apollo、AWS、GitHub、SAP 等公司的支持。

与 REST 不同,REST 中每个端点返回固定的数据结构,而 GraphQL 使用一个接收查询字符串的单一端点。客户端在查询中描述需要哪些字段,服务器则精确返回这些字段。例如,查询 { user(id: “1”) { name email } } 将只返回用户的 name 和 email,而不会包含在 REST 中需获取的 address、phone 或 createdAt 等额外字段。

GraphQL 不绑定于任何特定的数据库或语言。规范仅定义了查询和响应的格式。存在 Node.js(graphql-js、Apollo Server)、Kotlin(graphql-kotlin、Netflix DGS Framework)、Python(Graphene、Strawberry)、Ruby(graphql-ruby)等语言的服务器实现。客户端库可用于所有主流平台,包括适用于 iOS、Android 和 Web 的 Apollo Client。

GraphQL 如何工作

GraphQL 架构由三个关键组件组成:架构(Schema)、解析器(Resolvers)和执行引擎(GraphQL Engine)。架构确定哪些数据类型可用、可以执行哪些查询以及它们接受哪些参数。解析器是服务器上为架构的每个字段返回数据的函数。执行引擎接收传入的查询,根据架构进行验证,调用相应的解析器并构建响应。

查询的处理过程如下:

  • 客户端向 /graphql 发送带有 JSON 请求体 { “query”: “...” } 的 POST 请求
  • 服务器解析查询,构建 AST(抽象语法树)并根据架构进行验证
  • 引擎遍历 AST,为每个字段调用解析器并收集数据
  • 响应以 JSON 格式返回,严格匹配查询的结构

GraphQL 架构的主要优势是 字段级解析。在 REST 中,开发者要么获取资源的所有字段(可能包含多余数据),要么使用 ?fields=name,email 等扩展。在 GraphQL 中,这种过滤已内置于语言中:每个查询明确指定需要哪些字段,服务器精确返回这些字段。这对移动应用尤为重要,因为传输的数据量直接影响加载速度和流量消耗。

Query、Mutation 和 Subscription

GraphQL 定义了三种类型的操作,每种操作对应特定的交互场景。Query — 用于读取数据,类似于 REST 中的 GET。Mutation — 用于修改数据(创建、更新、删除),类似于 POST/PUT/DELETE。Subscription — 用于通过 WebSocket 进行实时更新,在经典 REST 中没有直接对应项(需要 WebSocket 或 Server-Sent Events 等额外解决方案)。

查询的基本语法直观易懂:

js
// 带参数的简单查询
query {
    user(id: "42") {
        name
        email
        avatarUrl
    }
}

// 返回已修改数据的变异
mutation {
    updateProfile(name: "伊万") {
        id
        name
        updatedAt
    }
}

// Subscription — 监听实时更新
subscription {
    newMessage(chatId: "chat_1") {
        id
        text
        sender { name }
    }
}

Query 并行执行 — 同一级别的所有字段同时加载。这允许通过一个查询加载相关数据(用户及其帖子),无需多次往返。Mutation 顺序执行 — 一个查询中的 mutation 按照声明顺序依次执行。Subscription 通过 WebSocket 建立持久连接,当事件发生时,服务器通过该连接发送数据。

操作可以接受 变量 以将数据与查询分离,指令(@include、@skip)以条件性地包含字段,以及 片段 以重用字段集合。这些能力使 GraphQL 查询灵活且可重用,这在包含多个屏幕和组件的大型项目中尤其重要。

GraphQL 架构和类型系统

GraphQL 的核心是 类型系统,它描述了所有可能的数据和 API 操作。架构(Schema)是服务器可以返回的类型以及接受的查询的描述。架构使用 Schema Definition Language (SDL) 编写,作为客户端和服务器之间的契约。客户端可以通过内省(introspection)获取架构 — 一个返回 API 完整描述的特殊查询 __schema。

博客架构示例:

js
// SDL — Schema Definition Language
type User {
    id: ID!
    name: String!
    email: String
    posts: [Post!]!
}

type Post {
    id: ID!
    title: String!
    content: String
    author: User!
}

type Query {
    user(id: ID!): User
    posts(page: Int): [Post!]!
}

感叹号 (!) 表示非空字段 — 保证在响应中存在。方括号 [ ] 表示列表。GraphQL 支持标量类型(Int、Float、String、Boolean、ID)、对象类型、枚举、联合、接口和输入类型(用于变异的参数)。严格的类型化使 API 自我文档化,并允许客户端工具生成代码:TypeScript 类型、Kotlin 数据类、Swift 结构体。

内省 — GraphQL 独有而 REST 中没有的能力。客户端可以向架构发送查询并接收所有类型、字段、参数和指令的完整描述。这是 GraphiQL 和 Apollo Studio 等工具的基础,这些工具自动为开发者生成文档和自动补全。内省还允许编写自动化测试,检查架构是否符合预期结构。

GraphQL 与 REST 的比较

GraphQL 和 REST 之间进行选择是设计 API 时的关键架构问题之一。两种方法各有优缺点,选择取决于项目的具体需求。REST 在简单性和通用性方面胜出,而 GraphQL 在灵活性和查询效率方面更优。让我们看看比较表格。

标准RESTGraphQL
响应结构固定,由服务器决定灵活,由客户端决定
Overfetching常见 — 服务器返回所有字段无 — 客户端只请求需要的字段
请求数量多次往返一次请求获取所有数据
缓存原生 HTTP 缓存需要手动配置
类型化未内置(取决于格式)严格,通过 SDL 架构
工具curl、Postman、SwaggerGraphiQL、Apollo Studio、内省
文件上传原生支持 multipart需要额外协议
性能可预测,更易优化取决于嵌套查询的复杂度

GraphQL 的主要缺点是 缓存困难。在 REST 中,HTTP 缓存在 URL 级别工作:一个对 /api/users/42 的请求总是返回相同的结构,响应可以根据 URL 缓存。在 GraphQL 中,所有请求都发送到一个端点,响应的结构取决于请求体。为了解决这个问题,Apollo Client 在客户端使用规范化缓存,将响应按 id 拆分为单独的实体,并在收到新数据时自动更新它们。

另一个重要方面是 N+1 问题。在请求嵌套数据时(例如,用户的帖子和每个帖子的评论),GraphQL 可能为列表中的每个元素执行单独的 SQL 查询。这可以通过 DataLoader 解决 — 一个用于批处理和缓存数据库查询的工具,它将单独的查询合并为一个批处理查询。在 REST 中,这个问题不那么明显,因为开发者在服务器端控制响应的结构。

GraphQL 查询示例

让我们看看在 Kotlin 移动应用中使用 Apollo Client 的 GraphQL 实际示例。这些示例展示了典型场景:为个人资料页面加载数据(query)、创建新帖子(mutation)和订阅新评论(subscription)。每个示例都包括 GraphQL 查询和客户端代码。

Query:加载个人资料及帖子

一个 GraphQL 查询加载用户、其最近的帖子以及关注者总数。在 REST 中,至少需要 2-3 个请求:/users/42、/users/42/posts、/users/42/stats。GraphQL 将它们合并为一次往返,从而在慢速连接上减少屏幕加载时间。

kotlin
// GraphQL 查询(在 .graphql 文件中)
query ProfileScreen($userId: ID!) {
    user(id: $userId) {
        name
        bio
        avatarUrl
        posts(limit: 10) {
            id
            title
            createdAt
        }
        followersCount
        followingCount
    }
}

// 客户端调用(Apollo Client + Kotlin)
val response = apolloClient
    .query(ProfileScreenQuery(userId = "42"))
    .execute()
binding.nameText.text = response.data?.user?.name

Mutation:创建新帖子

变异不仅创建资源,还返回其当前数据以更新 UI。__typename 字段被 Apollo Client 用于规范化缓存 — 在收到成功的变异响应后,客户端自动更新缓存中的 Post 记录。

kotlin
// GraphQL 变异
mutation CreatePost($input: CreatePostInput!) {
    createPost(input: $input) {
        id
        title
        createdAt
        author {
            id
            name
        }
    }
}

// 使用输入类型调用变异
val input = CreatePostInput(
    title = "关于 GraphQL 的新帖子",
    content = "GraphQL 简化了与 API 的工作……"
)
val result = apolloClient
    .mutation(CreatePostMutation(input))
    .execute()

在移动开发方面,GraphQL 相对于 REST 的一个重要优势是 自动代码生成。用于 Kotlin 的 Apollo Client(Apollo GraphQL)在构建阶段从 .graphql 文件生成类型安全的类。如果服务器更改了架构,项目在查询更新之前将无法构建。这防止了 REST 中常见的运行时错误,在 REST 中响应结构的更改在开发过程中可能不被注意。

生态系统:Apollo、Relay 和工具

GraphQL 生态系统包括几个关键的库和工具,它们简化了开发和运维。Apollo Client — 最流行的客户端库,支持 React、iOS、Android 和 Kotlin Multiplatform。Facebook 的 Relay — React 应用的替代方案,具有独特的数据管理和缓存方法。Apollo 和 Relay 之间的选择取决于平台和性能要求。

在服务器端,Apollo Server(Node.js)、Netflix DGS Framework(Kotlin/Java)和 graphql-ruby 处于领先地位。用于开发架构和测试查询的工具是 GraphiQL — 内置于浏览器的交互式 IDE。Apollo Studio 为生产环境提供性能指标、查询跟踪和架构管理。另外值得一提的是 GraphQL Code Generator — 一个从 SDL 架构生成 TypeScript、Kotlin、Swift 和 Dart 类型的工具。

对于移动开发,特别值得关注的是 Apollo Kotlin(Apollo GraphQL) — 一个完全用 Kotlin 编写的库,支持协程、Flow 和 Multiplatform。它允许在 Kotlin Multiplatform 项目中为 Android 和 iOS 使用相同的 GraphQL 查询。Apollo Kotlin 规范化缓存、支持字段级错误(partial errors)并自动从 .graphql 文件生成数据模型。这使得 GraphQL 成为大型移动项目的首选,因为在这些项目中开发速度和类型安全性非常重要。

常见问题

GraphQL 会取代 REST 吗?

GraphQL 不会取代 REST,而是提供了一种替代方法。REST 更适合简单的 CRUD API、通过 HTTP 缓存以及负载可预测的公共 API。GraphQL 适用于具有大量相关数据的复杂接口。

从 REST 迁移到 GraphQL 难吗?

迁移可以 逐步 进行:GraphQL 可以作为现有 REST 服务前面的中间层(网关)工作。许多公司在不关闭旧 API 的情况下将 GraphQL 与 REST 一起添加。完全替换需要重写解析器。

GraphQL 中的 N+1 问题是什么?

N+1 发生在对列表中的每个元素执行单独的数据库查询时。通过 DataLoader 解决——一个将单独的查询批处理为一个并在单个 HTTP 请求范围内缓存结果的库。

GraphQL 如何处理文件上传?

GraphQL 规范没有直接定义文件上传。实践中使用:base64 编码(简单但对大文件效率低)、根据 graphql-multipart-request-spec 协议的 multipart 请求,或单独的文件 REST 端点。

GraphQL 安全吗?

GraphQL 的安全性需要额外措施:限制嵌套深度、查询复杂度限制、操作级别的速率限制。公开的架构内省可能暴露数据结构——在生产环境中建议禁用它。

总结

  • GraphQL — 客户端控制响应结构的查询语言,消除了 overfetching 和 underfetching
  • 三种操作类型:query(读取)、mutation(写入)、subscription(实时)
  • 使用 单一端点 和严格的类型系统 — SDL 架构
  • 与 REST 不同,解决了 多次往返 问题 — 所有数据在一次查询中
  • 需要 DataLoader 来防止 N+1 问题以及手动配置缓存
  • 主要客户端:Apollo Client(Android、iOS、Web)和 Relay(React)
  • 最适合具有多个相关实体的 复杂接口 和移动应用

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读