使用 GraphQL 进行身份验证
您可以使用个人访问令牌、GitHub 应用或 OAuth 应用对 GraphQL API 进行身份验证。
使用个人访问令牌进行身份验证
要使用个人访问令牌进行身份验证,请按照“管理您的个人访问令牌”中的步骤操作。您请求的数据将决定您需要的范围或权限。
例如,选择“issues:read”权限以读取令牌有权访问的存储库中的所有问题。
所有细粒度的个人访问令牌都包含对公共存储库的读取访问权限。要使用个人访问令牌(经典版)访问公共存储库,请选择“public_repo”范围。
如果您的令牌没有访问资源所需的范围或权限,API 将返回一条错误消息,说明令牌需要的范围或权限。
使用 GitHub 应用进行身份验证
如果您想代表组织或其他用户使用 API,GitHub 建议您使用 GitHub 应用。为了将活动归因于您的应用,您可以让您的应用以应用安装的身份进行身份验证。为了将应用活动归因于用户,您可以让您的应用代表用户进行身份验证。在这两种情况下,您都将生成一个令牌,您可以使用该令牌对 GraphQL API 进行身份验证。有关更多信息,请参阅“注册 GitHub 应用”和“关于使用 GitHub 应用进行身份验证”。
使用 OAuth 应用进行身份验证
要使用来自 OAuth 应用的 OAuth 令牌进行身份验证,您必须首先使用 Web 应用程序流程或设备流程授权您的 OAuth 应用。然后,您可以使用收到的访问令牌访问 API。有关更多信息,请参阅“创建 OAuth 应用”和“授权 OAuth 应用”。
GraphQL 端点
REST API 具有众多端点。使用 GraphQL API 时,无论执行什么操作,端点都保持不变。对于 GitHub.com,该端点为
https://api.github.com/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 }}\" \
} \
" https://api.github.com/graphql
注意
`“query”` 的字符串值必须转义换行符,否则模式将无法正确解析它。对于 `POST` 主体,请使用外部双引号和转义的内部双引号。
关于查询和更改操作
GitHub 的 GraphQL API 中允许的两种操作类型是 *查询* 和 *更改*。将 GraphQL 与 REST 进行比较,查询类似于 `GET` 请求,而更改类似于 `POST` / `PATCH` / `DELETE`。 更改名称 确定执行哪个修改。
有关速率限制的信息,请参阅“GraphQL API 的速率限制和节点限制”。
查询和更改具有相似的形式,但也有一些重要的区别。
关于查询
GraphQL 查询仅返回您指定的数据。要形成查询,您必须指定 字段内的字段(也称为 *嵌套子字段*),直到您只返回 标量。
查询结构如下所示
query { JSON-OBJECT-TO-RETURN }
有关实际示例,请参阅“查询示例”。
关于更改
要形成更改,您必须指定三件事
- *更改名称*。您要执行的修改类型。
- *输入对象*。您要发送到服务器的数据,由 *输入字段* 组成。将其作为参数传递给更改名称。
- *有效负载对象*。您要从服务器返回的数据,由 *返回字段* 组成。将其作为更改名称的主体传递。
更改结构如下所示
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
}
使用变量有三个步骤
-
在 `variables` 对象中操作外部定义变量
variables { "number_of_repos": 3 }
该对象必须是有效的 JSON。此示例显示了一个简单的 `Int` 变量类型,但可以定义更复杂的变量类型,例如输入对象。您也可以在此处定义多个变量。
-
将变量作为参数传递给操作
query($number_of_repos:Int!){
参数是一个键值对,其中键是开头为 `$` 的 *名称*(例如,`$number_of_repos`),值是 *类型*(例如,`Int`)。添加 `!` 以指示类型是否为必需。如果您已定义多个变量,请在此处将其作为多个参数包含。
-
在操作中使用变量
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` 对象。模式验证表明此对象需要 `owner` 和 `name` 参数。
-
issues(last:20, states:CLOSED) {
为了考虑存储库中的所有问题,我们调用 `issues` 对象。(我们可以查询 `repository` 上的单个 `issue`,但这需要我们知道要返回的问题编号并将其作为参数提供。)
关于
issues
对象的一些细节- 文档此处告诉我们这个对象类型为
IssueConnection
。 - 模式验证表明此对象需要
last
或first
参数来指定结果数量,因此我们提供20
。 - 文档此处还告诉我们此对象接受
states
参数,这是一个IssueState
枚举,它接受OPEN
或CLOSED
值。为了仅查找已关闭的问题,我们将states
键的值设置为CLOSED
。
- 文档此处告诉我们这个对象类型为
-
edges {
我们知道
issues
是一个连接,因为它具有IssueConnection
类型。要检索有关单个问题的详细信息,我们必须通过edges
访问节点。 -
node {
在这里,我们检索边末端的节点。
IssueConnection
文档 指出IssueConnection
类型末端的节点是Issue
对象。 -
现在我们知道我们正在检索
Issue
对象,我们可以查看文档 并指定我们想要返回的字段。title url labels(first:5) { edges { node { name } } }
在这里,我们指定
Issue
对象的title
、url
和labels
字段。labels
字段的类型为LabelConnection
。与issues
对象一样,因为labels
是一个连接,所以我们必须沿着其边移动到连接的节点:label
对象。在节点处,我们可以指定我们想要返回的label
对象字段,在本例中为name
。
您可能会注意到,在 Octocat 的公共Hello-World
仓库上运行此查询不会返回许多标签。尝试在一个使用标签的您自己的仓库上运行它,您可能会看到差异。
示例变异
变异通常需要您只能通过首先执行查询才能找到的信息。此示例显示两个操作
- 获取问题 ID 的查询。
- 向问题添加表情符号反应的变异。
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
如果您为它们命名(在本例中为FindIssueID
和AddReactionToIssue
),则可以在同一个 Explorer 窗口中包含查询和变异,但这些操作将作为对 GraphQL 端点的单独调用执行。不可能同时执行查询和变异,反之亦然。
让我们逐步完成此示例。任务听起来很简单:向问题添加表情符号反应。
那么我们怎么知道要从查询开始呢?我们还不知道。
因为我们想要修改服务器上的数据(将表情符号附加到问题),所以我们首先在模式中搜索有用的变异。参考文档显示了addReaction
变异,其描述如下:Adds a reaction to a subject.
完美!
该变异的文档列出了三个输入字段
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
!
表示subjectId
和content
是必需字段。必需的content
很有意义:我们想要添加反应,因此我们需要指定要使用哪个表情符号。
但是为什么subjectId
是必需的呢?这是因为subjectId
是识别要对其做出反应的哪个仓库中的哪个问题的唯一方法。
这就是我们从查询开始此示例的原因:获取ID
。
让我们逐行检查查询
-
query FindIssueID {
在这里,我们正在执行查询,并将其命名为
FindIssueID
。请注意,命名查询是可选的;我们在这里为它命名是为了能够将其与变异一起包含在同一个 Explorer 窗口中。 -
repository(owner:"octocat", name:"Hello-World") {
我们通过查询
repository
对象并传递owner
和name
参数来指定仓库。 -
issue(number:349) {
我们通过查询
issue
对象并传递number
参数来指定要对其做出反应的问题。 -
id
在这里,我们检索
https://github.com/octocat/Hello-World/issues/349
的id
,以作为subjectId
传递。
当我们运行查询时,我们得到id
:MDU6SXNzdWUyMzEzOTE1NTE=
注意
查询中返回的id
是我们将作为subjectID
传递给变异的值。文档和模式内省都不会指示这种关系;您需要理解名称背后的概念才能弄清楚这一点。
已知 ID 后,我们可以继续进行变异
-
mutation AddReactionToIssue {
在这里,我们正在执行变异,并将其命名为
AddReactionToIssue
。与查询一样,命名变异是可选的;我们在这里为它命名是为了能够将其与查询一起包含在同一个 Explorer 窗口中。 -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
让我们检查这一行
addReaction
是变异的名称。input
是必需的参数键。对于变异,这始终是input
。{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}
是必需的参数值。这始终是一个输入对象(因此是大括号),由变异的输入字段(在本例中为subjectId
和content
)组成。
我们怎么知道要使用哪个值作为内容?
addReaction
文档 告诉我们content
字段的类型为ReactionContent
,这是一个枚举,因为 GitHub 问题上只支持某些表情符号反应。这些是允许的反应值(请注意,某些值与其对应的表情符号名称不同)内容 表情符号 +1
👍 -1
👎 laugh
😄 confused
😕 heart
❤️ hooray
🎉 rocket
🚀 eyes
👀 -
其余调用由有效负载对象组成。在这里,我们指定在执行变异后我们希望服务器返回的数据。这些行来自
addReaction
文档,其中有三个可能的返回字段clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
在此示例中,我们返回两个必需的字段(
reaction
和subject
),这两个字段都有必需的子字段(分别是content
和id
)。
当我们运行变异时,这是响应
{
"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 调用时,您可以执行的操作还有很多。以下是一些接下来可以查看的地方