使用 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 有一个端点
https://api.github.com/graphql
无论执行什么操作,端点都保持不变。
与 GraphQL 通信
由于 GraphQL 操作包含多行 JSON,GitHub 建议使用Explorer进行 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
。
在突变参考中,列出的输入字段是您作为输入对象传递的内容。列出的返回字段是您作为有效负载对象传递的内容。
有关实际示例,请参见“示例突变”。
使用变量
变量可以使查询更具动态性和更强大,并且可以在传递突变输入对象时降低复杂性。
注意:如果您使用 Explorer,请务必在单独的查询变量窗格中输入变量,并且不要在 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
}
}
}
虽然你可以在同一个 Explorer 窗口中包含一个查询和一个变异(在本例中为 FindIssueID
和 AddReactionToIssue
),但这些操作将作为对 GraphQL 端点的单独调用执行。不可能在与变异同时执行查询,反之亦然。
我们来浏览一下这个示例。任务听起来很简单:向问题添加表情反应。
那么我们如何知道以查询开始?我们还不知道。
因为我们想要修改服务器上的数据(将表情符号附加到问题),所以我们首先在模式中搜索有用的变异。参考文档显示 addReaction
变异,其描述为:向主题添加反应。
完美!
变异的文档列出了三个输入字段
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
👎 笑
😄 困惑
😕 心
❤️ 欢呼
🎉 火箭
🚀 眼睛
👀 -
调用的其余部分由有效负载对象组成。我们在此指定希望服务器在执行突变后返回的数据。这些行来自
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 调用时,您可以做的事情还有 很多。以下是一些接下来可以查看的地方