GraphQL 入门学习指南

一、核心概念与思想

这是理解 GraphQL 的基石,关键在于思维模式的转变。

1.1 核心思想:从"自助餐厅"到"点餐"

  • REST API (自助餐厅): 服务器提供一系列固定的套餐 (Endpoints),如 /users/1/users/1/posts。客户端只能选择这些套餐,常常会拿到不需要的数据(过度获取),或者需要多次选择才能凑齐想要的数据(多次请求)。
  • GraphQL (点餐): 服务器只提供一张详尽的菜单 (Schema)。客户端按需填写一张点餐单(一次请求),精确地描述所有需要的数据。服务器一次性地、不多不少地将所有内容返回。

1.2 Schema: API 的唯一"菜单" (The Single Source of Truth)

Schema 是前后端之间最重要的"合同",它使用 Schema Definition Language (SDL) 定义了 API 的所有能力。

1.3 Schema 的构成要素

  • 类型 (Types):

    • 标量类型 (Scalar Types): GraphQL 内置的基础类型: String, Int, Float, Boolean, ID

    • 对象类型 (Object Types): 根据业务需求自定义的复杂类型,如 User, Post

    • 类型修饰符 ![]: 这是定义类型时至关重要的部分。

      • ! (感叹号): 代表**"非空 (Non-Null)"**。加了 ! 的字段,服务器承诺返回结果时该字段的值绝不会是 null
      • [] (方括号): 代表**"列表 (List)"**,即一个数组。
    • 组合使用 ![]:

      • [User]: 可空列表,元素也可空 (很少用)。
      • [User!]: 可空列表,元素非空 (常用)。
      • [User]!: 非空列表,元素可空。
      • [User!]!: 非空列表,元素非空 (最佳实践)。
  • 三大根类型 (Root Types): API 的操作入口。

    • Query: "读"操作入口。
    • Mutation: "写"操作入口。
    • Subscription: 实时数据入口。
  • 如何表示"没有数据":

    • 查询单个对象: 如果对象不存在 (如 user(id: "not-found")),Schema 中应定义其返回类型为可空 (如 user: User),服务器应返回 null
    • 查询列表: 如果查询结果为空 (如 users(filter: {name: "nobody"})),Schema 中应定义其返回类型为列表 (如 users: [User!]!),服务器应返回一个空列表 []

1.4 查询语言 (Query Language)

客户端用来"点餐"的语言,其核心是"所见即所得"。

  • 查询结构:

    • 字段 (Fields): 按需选择。
    • 参数 (Arguments): field(key: value)
    • 嵌套 (Nesting): {} 获取关联数据。

1.5 操作的两种形式:匿名操作 vs. 具名操作

在编写查询或变更时,有两种形式,理解它们的区别对于使用变量至关重要。

  • 匿名操作 (Anonymous Operation):

    这是一种简写形式,直接以 { 开头。它简单快捷,非常适合在工具中进行快速、一次性的测试。

    GraphQL 复制代码
    # 这是一个匿名查询
    {
      character(id: "1") {
        name
      }
    }

    限制: 匿名操作不支持定义或使用变量。

  • 具名操作 (Named Operation):

    这是完整的、更规范的写法,由三部分构成:操作类型 操作名称(变量定义) { ... }。

    • 操作类型: query, mutation, 或 subscription

    • 操作名称: 你为这个操作取的名字,如 GetCharacterById,便于调试和日志记录。

    • 变量定义:() 中定义该操作需要的所有变量。

    GraphQL 复制代码
    # 这是一个具名查询,但没有变量
    query GetCharacterName {
      character(id: "1") {
        name
      }
    }
    
    # 这是一个带变量的具名查询
    query GetCharacterById($charId: ID!) {
      character(id: $charId) {
        name
      }
    }

    核心规则:只要你想在操作中使用动态变量,就必须使用"具名操作"的完整形式。

1.6 基础语法规则

  • 无通配符 *: 必须明确列出所有需要的字段。
  • 引号规则: 字符串必须 使用双引号 (")
  • 输入 vs 输出: 输入参数用 key: value,输出字段只需 key

二、在线实战练习

使用 Rick and Morty GraphQL API 作为在线练习平台。

1. 基础查询

获取第一个角色的 name, species, gender

GraphQL 复制代码
query { character(id: 1) { name, species, gender } }

2. 嵌套查询

获取 Morty (id: 2) 的 name 及其 originlocationname

GraphQL 复制代码
query { character(id: 2) { name, origin { name }, location { name } } }

3. 列表与参数

查找所有名为 "Jerry Smith" 的角色的 idname

GraphQL 复制代码
query { characters(filter: { name: "Jerry Smith" }) { results { id, name } } }

4. 别名 (Alias)

一次请求同时获取 id为 1 和 2 的角色,并重命名为 rickmorty

GraphQL 复制代码
query { rick: character(id: 1) { name }, morty: character(id: 2) { name } }

5. 片段 (Fragment)

使用片段优化上一个查询,避免重复书写字段。

GraphQL 复制代码
query {
  rick: character(id: 1) { ...CharacterInfo }
  morty: character(id: 2) { ...CharacterInfo }
}
fragment CharacterInfo on Character { name, status, species, gender }

6. 变量 (Variables)

编写一个通用查询,通过变量动态传入 id

Query:

GraphQL 复制代码
query GetCharacterById($charId: ID!) { character(id: $charId) { name, image } }

Variables (JSON):

JSON 复制代码
{ "charId": "183" }

三、进阶技巧与核心规则释疑

本节将详细阐述我们之前讨论过的所有进阶问题。

3.1 深入理解参数与变量的多种情况

  • 场景一:为一个参数提供多个"条件" (多条件过滤)

    • 规则: 将多个条件作为键值对,放在同一个 filter 对象中。它们之间的逻辑关系为 AND

    • 示例: 查询所有物种为 "Human" 且状态为 "Alive" 的角色。

      GraphQL 复制代码
      query {
        characters(filter: { species: "Human", status: "Alive" }) {
          results {
            name
            species
            status
          }
        }
      }
  • 场景二:一次性查询多个 ID (输入数组参数)

    • 规则: 直接在参数中以内联方式或通过变量传入一个数组 []

    • 示例: 使用 charactersByIds 查询 id 为 1 和 2 的角色。

      • 硬编码方式:

        GraphQL 复制代码
        query {
          charactersByIds(ids: ["1", "2"]) {
            id
            name
          }
        }
      • 变量方式 (Query):

        GraphQL 复制代码
        query GetCharsByIds($idList: [ID!]!) {
          charactersByIds(ids: $idList) {
            id
            name
          }
        }
      • 变量方式 (Variables JSON):

        JSON 复制代码
        { "idList": ["1", "2"] }
  • 场景三:为一个请求提供多个"动态变量"

    • 规则:query 定义的 () 中用逗号 , 分隔多个变量声明;在 Variables JSON 中用逗号 , 分隔多个键值对。

    • 示例: 同时查询 id 为 2 的角色,以及所有状态为 "Alive" 的角色。

      • Query:

        GraphQL 复制代码
        query GetCharacterAndFilter($charId: ID!, $status: String!) {
          specificCharacter: character(id: $charId) {
            id
            name
          }
          filteredCharacters: characters(filter: { status: $status }) {
            results {
              id
              name
            }
          }
        }
      • Variables JSON:

        JSON 复制代码
        {
          "charId": "2",
          "status": "Alive"
        }

3.2 链式查询 vs. 并行查询 ("一次请求到底能查多少东西?")

  • 核心规则: 在一次请求的同一层级 ,你可以调用任意多个相互独立的"方法"(字段) 。但你不能将一个字段的输出结果作为同一请求内另一个字段的输入(链式依赖)。

  • 并行查询 (Query): query 中的多个顶级字段(如 character, location)是相互独立的,服务器可以并行执行它们以提高效率。

    GraphQL 复制代码
    query {
      # 这三个字段会并行执行
      character(id: "1") { name }
      location(id: "3") { name }
      episode(id: "5") { name }
    }
  • 链式查询 (不支持): "先查A,再用A的结果查B"的逻辑,必须由客户端分两次请求完成。

3.3 mutation 的串行执行规则

  • 规则: mutation 中的多个顶级字段,即使在结构上是独立的,也必须由服务器严格按照其书写顺序,串行(一个接一个)执行

  • 目的: 这是为了保证数据修改的逻辑是可预测的,避免因并行执行而导致的数据错乱(竞态条件)。

    GraphQL 复制代码
    mutation {
      # 服务器保证先执行这个
      op1: someCreateOperation(...) { id }
      # 再执行这个
      op2: someUpdateOperation(...) { id }
    }

3.4 片段 (...) 与指令 (@)

  • ... (片段展开): 用于代码复用,特别适合与组件化的前端框架结合。

  • @ (指令): 用于在运行时改变查询行为。

    • @include(if: Boolean): 当 iftrue 时,包含该字段。
    • @skip(if: Boolean): 当 iftrue 时,跳过该字段。

四、工具使用:Postman

  • 核心规则: 方法永远是 POST ,URL 永远是单一端点 ,所有内容都在 Body 中。

  • 操作步骤:

    1. 新建请求: 创建一个新请求,方法设为 POST,填入 URL (例如: https://rickandmortyapi.com/graphql)。
    2. 配置 Body: 点击 Body 标签页,选择 GraphQL 选项。
    3. 填入内容:QueryGRAPHQL VARIABLES 输入框中分别粘贴查询语句和 JSON 变量。
    4. 发送请求: 点击 Send

五、官方与推荐学习资源

相关推荐
星光一影4 小时前
HIS系统天花板,十大核心模块,门诊/住院/医保全流程打通,医院数字化转型首选
java·spring boot·后端·sql·elementui·html·scss
武子康4 小时前
大数据-126 - Flink一文搞懂有状态计算:State Backend 工作原理与性能差异详解 核心原理与作用
大数据·后端·flink
Zz_waiting.5 小时前
Spring Cloud 概述
后端·spring·spring cloud
supermiketho5 小时前
springboot 实现websocket通信
spring boot·后端·websocket
Kiri霧5 小时前
在actix-web中创建一个提取器
后端·rust·web
^_^ 纵歌5 小时前
rust主要用于哪些领域
开发语言·后端·rust
JaguarJack5 小时前
现代 PHP8+ 实战特性介绍 Enums、Fibers 和 Attributes
后端·php
绝无仅有5 小时前
面试真实经历某商银行大厂Java问题和答案总结(四)
后端·面试·github
绝无仅有5 小时前
面试真实经历某商银行大厂Java问题和答案总结(六)
后端·面试·github