Skip to main content
我们经常发布文档更新,此页面的翻译可能仍在进行中。 有关最新信息,请访问英语文档

使用 GraphQL 建立调用

了解如何向 GraphQL API 验证身份,以及如何创建并运行查询和突变。

使用 GraphQL 进行身份验证

注意:需要创建 personal access token、GitHub App 或 OAuth App 才能向 GraphQL API 进行身份验证。 GraphQL API 不支持使用 fine-grained personal access token 进行身份验证。

使用 Personal access token 进行身份验证

若要使用 personal access token 进行身份验证,请按照“创建个人访问令牌”中的步骤创建一个 personal access token。 你请求的数据将指示需要哪些范围。 例如,选择“read:user”范围可以请求有关用户的数据。 选择“public_repo”范围可以请求有关公共存储库的数据。

如果令牌没有访问资源所需的范围,API 将返回一条错误消息,指出令牌所需的范围。

使用 GitHub App 进行身份验证

如果你要代表组织或其他用户使用 API,GitHub 建议使用 GitHub App。 若要以 GitHub App 的身份进行身份验证,首先必须生成一个 PEM 格式的私钥。 然后,必须使用此密钥对 JSON Web 令牌 (JWT) 进行签名。 可以使用 JSON Web 令牌从 GitHub 请求安装令牌,它可用于向 GraphQL API 进行身份验证。 有关详细信息,请参阅“创建 GitHub 应用程序”、“About authentication with a GitHub App”和“代表用户使用 GitHub 应用进行身份验证”。

使用 OAuth App 进行身份验证

若要使用来自 OAuth App 的 OAuth 令牌进行身份验证,必须先使用 Web 应用程序流或设备流授权 OAuth App。 然后,可以使用收到的访问令牌来访问 API。 有关详细信息,请参阅“创建 OAuth 应用程序”和“授权 OAuth 应用”。

GraphQL 端点

REST API 有多个端点;GraphQL API 只有一个端点:

http(s)://HOSTNAME/api/graphql

无论执行什么操作,端点都保持不变。

与 GraphQL 通信

由于 GraphQL 操作由多行 JSON 组成,因此 GitHub 建议使用浏览器进行 GraphQL 调用。 也可以使用 curl 或任何其他采用 HTTP 的库。

在 REST 中,HTTP 谓词确定执行的操作。 在 GraphQL 中,无论是执行查询还是突变,都要提供 JSON 编码的正文,因此 HTTP 谓词为 POST。 唯一的例外是内省查询,它是一种简单的 GET 到终结点查询。 有关 GraphQL 与 REST 的更多信息,请参阅“从 REST 迁移到 GraphQL”。

要使用 curl 命令查询 GraphQL,请利用 JSON 有效负载发出 POST 请求。 有效负载必须包含一个名为 query 的字符串:

curl -H "Authorization: bearer TOKEN" -X POST -d " \
 { \
   \"query\": \"query { viewer { login }}\" \
 } \
" http(s)://HOSTNAME/api/graphql

注意:"query" 的字符串值必须进行换行符转义,否则架构将无法正确解析它。 对于 POST 正文,请使用外双引号和转义的内双引号。

关于查询和突变操作

GitHub 的 GraphQL API 中允许的两种操作类型为查询和突变 。 比较 GraphQL 与 REST,查询操作就像 GET 请求,而突变操作则像 POST/PATCH/DELETE突变名称确定执行的修改内容。

有关速率限制的信息,请参阅“资源限制”。

查询和突变形式相似,但有一些重要差异。

关于查询

GraphQL 查询仅返回你指定的数据。 要建立查询,必须指定字段内的字段(也称为嵌套的子字段),直到仅返回标量

查询的结构如下:

query {
  JSON-OBJECT-TO-RETURN
}

有关实际示例,请参阅“示例查询”。

关于突变

要建立突变,必须指定三个参数:

  1. 突变名称。 您要执行的修改类型。
  2. 输入对象。 要发送到服务器的数据,由输入字段组成。 将其作为参数传递至突变名称。
  3. 有效负载对象。 要从服务器返回的数据,由返回字段组成。 将其作为突变名称的正文传递。

突变的结构如下:

mutation {
  MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) {
    MUTATION-NAME-PAYLOAD
  }
}

此示例中的输入对象为 MutationNameInput,有效负载对象为 MutationNamePayload

突变引用中,列出的输入字段是作为输入对象传递的内容。 列出的返回字段是作为有效负载对象传递的内容。

有关实际示例,请参阅“示例突变”。

使用变量

变量可使查询更具动态和更加强大,并且可以在传递突变输入对象时降低复杂性。

注意:如果使用的是此浏览器,请确保在单独的“查询变量”窗格中输入变量,且 JSON 对象之前不含 variables 一词。

下面是一个单变量查询示例:

query($number_of_repos:Int!) {
  viewer {
    name
     repositories(last: $number_of_repos) {
       nodes {
         name
       }
     }
   }
}
variables {
   "number_of_repos": 3
}

使用变量包含三个步骤:

  1. variables 对象中定义操作以外的变量:

    variables {
       "number_of_repos": 3
    }
    

    此对象必须是有效的 JSON。 本示例显示了一个简单的 Int 变量类型,但可以定义更复杂的变量类型,如输入对象。 也可以在此定义多个变量。

  2. 将变量作为参数传递至操作:

    query($number_of_repos:Int!){
    

    此参数是一个键值对,其中键为以 $ 开头的名称(例如 $number_of_repos),值为类型(例如 Int) 。 添加 ! 以指出是否需要此类型。 如果您已经定义了多个变量,请将它们作为多个参数加入此处。

  3. 在操作中使用变量:

    repositories(last: $number_of_repos) {
    

    在本示例中,我们用变量替换要检索的仓库编号。 在步骤 2 中指定类型,因为 GraphQL 会强制执行强类型化。

此流程会使查询参数具有动态性。 现在,只需更改 variables 对象中的值,查询的其余部分则保持不变。

将变量用作参数可支持动态更新 variables 对象中的值,而无需更改查询。

示例查询

我们来演练一个较为复杂的查询,并将此信息放在上下文中。

下面的查询用于查找 octocat/Hello-World 存储库,查找 20 个最近关闭的问题,并返回每个问题的标题、URL 和前 5 个标签:

query {
  repository(owner:"octocat", name:"Hello-World") {
    issues(last:20, states:CLOSED) {
      edges {
        node {
          title
          url
          labels(first:5) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    }
  }
}

逐行查看此查询的组成元素:

  • query {

    由于我们想从服务器读取数据,而不是修改数据,因此 query 是根操作。 (如果未指定操作,则 query 也是默认操作。)

  • repository(owner:"octocat", name:"Hello-World") {

    要开始查询,需找到 repository 对象。 架构验证表明此对象需要 ownername 参数。

  • issues(last:20, states:CLOSED) {

    为考虑到存储库中的所有问题,我们调用 issues 对象。 (可查询 repository 中的单个 issue,但这需要了解我们想返回的问题编号,并将其作为参数。)

    有关 issues 对象的一些详细信息:

    • 文档中指出,此对象的类型为 IssueConnection
    • 架构验证表明此对象需要将 lastfirst 个结果作为参数,因此我们提供了 20
    • 文档也指出,此对象接受 states 参数,即一种 IssueState 枚举类型,可接受的值为 OPENCLOSED。 要仅查找已关闭的问题,可对 states 键赋值 CLOSED
  • edges {

    我们知道 issues 是一种连接,因为它的类型为 IssueConnection。 要检索关于各个问题的数据,必须通过 edges 访问节点。

  • node {

    在本示例中,我们将检索边缘末尾的节点。 IssueConnection 文档指示 IssueConnection 类型末尾的节点为 Issue 对象

  • 了解了要检索 Issue 对象后,现在可以查看文档并指定要返回的字段:

    title
    url
    labels(first:5) {
      edges {
        node {
          name
        }
      }
    }
    

    在此指定 Issue 对象的 titleurllabels 字段。

    labels 字段的类型为 LabelConnection。 与 issues 对象一样,labels 也是一种连接,因此必须将其边缘传送到连接的节点:label 对象。 在此节点上,可指定要返回的 label 对象字段,在本例中为 name

你可能会注意到,在 Octocat 的公共 Hello-World 存储库中运行此查询不会返回很多标签。 尝试在您自己的其中一个使用标签的仓库中运行,很可能会看到不同的结果。

突变示例

突变通常需要只有先执行查询才能找到的信息。 本示例显示两个操作:

  1. 用于获取议题 ID 的查询。
  2. 用于向议题添加表情符号反应的突变。
query FindIssueID {
  repository(owner:"octocat", name:"Hello-World") {
    issue(number:349) {
      id
    }
  }
}

mutation AddReactionToIssue {
  addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}

如果为查询和突变命名(在本示例中为 FindIssueIDAddReactionToIssue),则可以将二者放入此浏览器的同一个窗口,但操作将作为对 GraphQL 终结点的单独调用执行。 不能同时执行查询和突变,反之亦然。

我们演练一遍这个示例。 任务听起来简单:向议题添加表情符号反应即可。

那么,我们怎么知道从查询开始呢? 还不知道。

因为我们想修改服务器上的数据(向议题添加表情符号),所以先搜索架构,查找有用的突变。 参考文档所示为 addReaction 突变,其描述为:Adds a reaction to a subject. Perfect!

突变文档列出了三个输入字段:

  • clientMutationId (String)
  • subjectId (ID!)
  • content (ReactionContent!)

! 指示 subjectIdcontent 是必填字段。 必填字段 content 是有道理的:我们想添加反应,因此需要指定要使用哪个表情符号。

subjectId 为什么是必填字段呢? 这是因为,subjectId 是确定要对哪个存储库中的哪个问题做出反应的唯一方式 。

因此,本示例要从查询开始:获取 ID

让我们逐行检查查询:

  • query FindIssueID {

    我们将执行查询,并将其命名为 FindIssueID。 请注意,为查询命名是可选操作;我们在此为它命名,然后即可将它与突变放在同一个 Explorer 窗口中。

  • repository(owner:"octocat", name:"Hello-World") {

    通过查询 repository 对象并传递 ownername 参数来指定存储库。

  • issue(number:349) {

    通过查询 issue 对象和传递 number 参数来指定要做出反应的问题。

  • id

    我们将检索 https://github.com/octocat/Hello-World/issues/349id,并作为 subjectId 传递。

运行查询时,将获取 idMDU6SXNzdWUyMzEzOTE1NTE=

注意:查询中返回的 id 是将在突变中作为 subjectID 传递的值。 文档和架构内省都不会显示这种关系;您需要理解这些名称背后的概念才能找出答案。

在 ID 已知的情况下,可以继续进行突变操作:

  • mutation AddReactionToIssue {

    我们将执行突变,并将其命名为 AddReactionToIssue。 与查询一样,为突变命名是可选操作;我们在此为它命名,然后即可将它与查询放在同一个 Explorer 窗口中。

  • addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {

    让我们来检查这一行:

    • addReaction 是突变的名称。

    • input 是必需的参数键。 突变的参数键始终是 input

    • {subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY} 是必需的参数值。 突变的参数值始终是由输入字段(在本例中为 subjectIdcontent)组成的输入对象(因此带有大括号)。

      我们怎么知道内容使用哪个值呢? addReaction 文档指出,content 字段的类型为 ReactionContent,即一种枚举类型,因为 GitHub 只支持使用某些表情符号进行反应。 这些是允许的反应值 (注意,某些值与其相应的表情符号名称不同):

      内容 表情符号
      +1 👍
      -1 👎
      laugh 😄
      confused 😕
      heart ❤️
      hooray 🎉
      rocket 🚀
      eyes 👀
  • 调用的其余部分由有效负载对象组成。 我们将在此指定执行突变后由服务器返回的数据。 这几行来自 addReaction 文档,其中包含三个可能返回的字段:

    • clientMutationId (String)
    • reaction (Reaction!)
    • subject (Reactable!)

    在本示例中,返回两个必填字段(reactionsubject),二者均包含必填子字段(分别为 contentid)。

我们运行突变时,响应如下:

{
  "data": {
    "addReaction": {
      "reaction": {
        "content": "HOORAY"
      },
      "subject": {
        "id": "MDU6SXNzdWUyMTc5NTQ0OTc="
      }
    }
  }
}

就这么简单! 将鼠标悬停在 🎉 上找到你的用户名,查看问题的反应

最后注意:当您在输入对象中传递多个字段时,语法可能会变笨拙。 将字段移入变量可避免这种情况。 下面是您利用变量重写原始突变的方式:

mutation($myVar:AddReactionInput!) {
  addReaction(input:$myVar) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}
variables {
  "myVar": {
    "subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
    "content":"HOORAY"
  }
}

你可能会注意到,前文示例中的 content 字段值(直接用于突变)在 HOORAY 两侧没有引号,但在变量中使用时有引号。 原因是:

  • 直接在突变中使用 content 时,架构预计此值的类型为 ReactionContent,即一种枚举类型,而非字符串。 如果您在枚举值两侧添加引号,架构验证将出现错误,因为引号是为字符串保留的。
  • 在变量中使用 content 时,变量部分必须为有效的 JSON,因此需要引号。 当变量在执行过程中传递至突变时,架构验证将正确解释 ReactionContent 类型。

有关枚举与字符串之间差异的更多信息,请参阅官方 GraphQL 规格

延伸阅读

建立 GraphQL 调用时,可执行更多操作。 下面是接下来要阅读的一些内容: