Autenticar com o GraphQL
Você pode se autenticar na API do GraphQL usando um personal access token, um GitHub App ou um OAuth app.
Autenticar com um personal access token
Para se autenticar com um personal access token, siga as etapas descritas em "Gerenciar seus tokens de acesso pessoal". Os dados que você está solicitando determinarão as permissões ou os escopos necessários.
Por exemplo, selecione a permissão "issues:read" para ler todos os problemas nos repositórios aos quais o token tem acesso.
Todos os fine-grained personal access tokens incluem acesso de leitura em repositórios públicos. Para acessar repositórios públicos com um personal access token (classic), selecione o escopo "public_repo".
Se o token não tiver as permissões nem os escopos necessários para acessar um recurso, a API retornará uma mensagem de erro informando as permissões ou os escopos dos quais o token precisa.
Autenticação com um GitHub App
Se você quiser usar a API em nome de uma organização ou de outro usuário, a GitHub recomenda que você use um GitHub App. Para atribuir a atividade ao seu aplicativo, você pode fazer com que o aplicativo se autentique como uma instalação de aplicativo. Para atribuir a atividade do aplicativo a um usuário, você pode fazer com que o aplicativo se autentique em nome de um usuário. Em ambos os casos, você vai gerar um token que pode ser usado para se autenticar na API do GraphQL. Para obter mais informações, confira "Registrar um Aplicativo GitHub" e "Sobre a autenticação com um GitHub App."
Como se autenticar com um OAuth app
Para se autenticar com um token OAuth por meio de um OAuth app, primeiro, você precisa autorizar o OAuth app usando um fluxo de aplicativo Web ou um fluxo de dispositivo. Em seguida, você pode usar o token de acesso que recebeu para acessar a API. Para obter mais informações, confira "Criar um aplicativo OAuth" e "Autorizar aplicativos OAuth."
O ponto final do GraphQL
A API REST tem vários pontos de extremidade. Com a API do GraphQL, o ponto de extremidade permanece constante, seja qual for a operação executada. Para o GitHub.com, esse ponto de extremidade é:
https://api.github.com/graphql
Se você acessar o GitHub em um domínio diferente, como octocorp.ghe.com
, o ponto de extremidade refletirá esse domínio. Por exemplo: https://api.octocorp.ghe.com/graphql
.
Comunicação com o GraphQL
Como as operações do GraphQL consistem em várias linhas de JSON, o GitHub recomenda o uso do Explorer para fazer chamadas ao GraphQL. Você também pode usar curl
ou qualquer outra biblioteca que fale HTTP.
Na REST, os verbos HTTP determinam a operação executada. No GraphQL, você fornecerá um texto codificado por JSON se estiver realizando uma consulta ou uma mutação. Portanto, o verbo HTTP é POST
. A exceção a isso é uma consulta de introspecção, que é um GET
simples para o ponto de extremidade. Para obter mais informações sobre uma comparação entre o GraphQL e a REST, confira "Fazer a migração de REST para o GraphQL".
Para consultar o GraphQL em um comando curl
, faça uma solicitação POST
com uma carga JSON. O conteúdo precisa conter uma cadeia de caracteres chamada query
:
curl -H "Authorization: bearer TOKEN" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" https://api.github.com/graphql
Observação: o valor da cadeia de caracteres "query"
precisa fazer o escape de caracteres de nova linha ou o esquema não o analisará corretamente. Para o corpo de POST
, use aspas duplas externas e aspas duplas internas com escape.
Sobre consultas e operações de mutação
Os dois tipos de operações permitidas na API do GraphQL do GitHub são consultas e mutações. Comparando o GraphQL com a REST, as consultas operam como solicitações GET
, enquanto as mutações operam como POST
/PATCH
/DELETE
. O nome da mutação determina a modificação que é executada.
Para obter informações sobre a limitação de taxa, confira "Limites de taxa e limites de nó para a API GraphQL".
As consultas e mutações compartilham formas semelhantes, com algumas diferenças importantes.
Sobre consultas
As consultas do GraphQL retornam apenas os dados especificados. Para formar uma consulta, você precisa especificar campos dentro de campos (também conhecido como subcampos aninhados) até retornar apenas escalares.
As consultas são estruturadas da seguinte forma:
query { JSON-OBJECT-TO-RETURN }
Para ver um exemplo do mundo real, confira "Exemplo de consulta".
Sobre as mutações
Para formar uma mutação, você deve especificar três coisas:
- Nome da mutação. O tipo de modificação que você deseja realizar.
- Objeto de entrada. Os dados que você deseja enviar para o servidor, compostos de campos de entrada. Passe-o como um argumento para o nome de mutação.
- Objeto de conteúdo. Os dados que você deseja retornar do servidor, compostos de campos de retorno. Passe-o como o texto do nome da mutação.
As mutações são estruturadas da seguinte forma:
mutation { MUTATION-NAME(input: {MUTATION-NAME-INPUT!}) { MUTATION-NAME-PAYLOAD } }
O objeto de entrada deste exemplo é MutationNameInput
, e o objeto de conteúdo é MutationNamePayload
.
Na referência de mutações, os campos de entrada listados são o que você transmite como o objeto de entrada. Os campos de retorno listados são o que você transmite como o objeto de conteúdo.
Para ver um exemplo do mundo real, confira "Exemplo de mutação".
Trabalhar com variáveis
As variáveis podem tornar as consultas mais dinâmicas e eficientes, além de reduzir a complexidade ao transmitir objetos de entrada de mutação.
Observação: se você estiver usando o Explorer, insira variáveis no painel Variáveis de Consulta separadas e não inclua a palavra variables
antes do objeto JSON.
Aqui está um exemplo de consulta com uma única variável:
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
Existem três etapas para usar variáveis:
-
Defina a variável fora da operação em um objeto
variables
:variables { "number_of_repos": 3 }
O objeto deve ser um JSON válido. Esse exemplo mostra um tipo de variável
Int
simples, mas é possível definir tipos de variáveis mais complexos, como objetos de entrada. Você também pode definir diversas variáveis aqui. -
Passe a variável para a operação como argumento:
query($number_of_repos:Int!){
O argumento é um par chave-valor, em que a chave é o nome que começa com
$
(por exemplo,$number_of_repos
), e o valor é o tipo (por exemplo,Int
). Adicione um!
para indicar se o tipo é obrigatório. Se você definiu diversas variáveis, inclua-as aqui como múltiplos argumentos. -
Use a variável dentro da operação:
repositories(last: $number_of_repos) {
Neste exemplo, substituímos a variável pelo número de repositórios a ser recuperados. Especificamos um tipo na etapa 2, porque o GraphQL impõe uma digitação forte.
Este processo torna o argumento da consulta dinâmico. Agora, podemos simplesmente alterar o valor no objeto variables
e manter o restante da consulta inalterado.
O uso de variáveis como argumentos permite que você atualize os valores dinamicamente no objeto variables
sem alterar a consulta.
Consulta de exemplo
Vamos analisar uma questão mais complexa e colocar essas informações no contexto.
A seguinte consulta faz uma pesquisa no repositório octocat/Hello-World
, encontra os 20 problemas fechados mais recentes e retorna o título, a URL e os cinco primeiros rótulos de cada problema:
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
Observando a composição linha por linha:
-
query {
Como queremos ler os dados do servidor e não modificá-los,
query
é a operação raiz. (Se você não especificar uma operação,query
também será o padrão). -
repository(owner:"octocat", name:"Hello-World") {
Para iniciar a consulta, queremos encontrar um objeto
repository
. A validação de esquema indica que esse objeto exige umowner
e um argumentoname
. -
issues(last:20, states:CLOSED) {
Para responder por todos os problemas no repositório, chamamos o objeto
issues
. (Podemos consultar um sóissue
em umrepository
, mas, para isso, precisaremos saber o número do problema que queremos retornar e fornecê-lo como um argumento).Alguns detalhes sobre o objeto
issues
:- A documentação nos informa que esse objeto tem o tipo
IssueConnection
. - A validação de esquema indica que esse objeto exige um número de resultados
last
oufirst
como argumento, ou seja, fornecemos20
. - A documentação também nos informa que esse objeto aceita um argumento
states
, que é uma enumeraçãoIssueState
que aceita valoresOPEN
ouCLOSED
. Para localizar apenas os problemas fechados, damos à chavestates
um valor igual aCLOSED
.
- A documentação nos informa que esse objeto tem o tipo
-
edges {
Sabemos que
issues
é uma conexão porque ela tem o tipoIssueConnection
. Para recuperar dados sobre problemas individuais, precisamos acessar o nó por meio deedges
. -
node {
Aqui, recuperamos o nó no fim da borda. A documentação de
IssueConnection
indica que o nó no final do tipoIssueConnection
é um objetoIssue
. -
Agora que sabemos que estamos recuperando um objeto
Issue
, podemos dar uma olhada na documentação e especificar os campos que queremos retornar:title url labels(first:5) { edges { node { name } } }
Aqui, especificamos os campos
title
,url
elabels
do objetoIssue
.O campo
labels
tem o tipoLabelConnection
. Assim como acontece com o objetoissues
, comolabels
é uma conexão, precisamos percorrer as bordas até um nó conectado: o objetolabel
. No nó, podemos especificar os campos de objetolabel
que queremos retornar, nesse caso,name
.
Você pode notar que a execução dessa consulta no repositório público Hello-World
do Octocat não retornará muitos rótulos. Tente executá-la em um dos seus próprios repositórios que usam etiquetas, e provavelmente você verá uma diferença.
Exemplo de mutação
De modo geral, as mutações exigem informações que você só pode encontrar ao realizar primeiro uma consulta. Este exemplo mostra duas operações:
- Uma consulta para obter um ID do problema.
- Uma mutação para adicionar uma reação de emojis ao problema.
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
Embora você possa incluir uma consulta e uma mutação na mesma janela do Explorer, se você der nomes a elas (FindIssueID
e AddReactionToIssue
, neste exemplo), as operações serão executadas como chamadas separadas para o ponto de extremidade do GraphQL. Não é possível realizar uma consulta junto com uma mutação ou vice-versa.
Vamos analisar o exemplo. A tarefa parece simples: adicione uma reação emoji a um problema.
Então, como é que sabemos começar com uma consulta? Ainda não sabemos.
Uma vez que desejamos modificar os dados no servidor (anexar um emoji a um problema), começamos procurando uma mutação útil no esquema. A documentação de referência mostra a mutação addReaction
, com esta descrição: Adds a reaction to a subject.
Perfeito!
A documentação para a lista de mutação lista três campos de entrada:
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
Os !
s indicam que subjectId
e content
são campos obrigatórios. Um content
obrigatório faz sentido: desejamos adicionar uma reação e, portanto, precisamos especificar o emoji que deve ser usado.
Mas por que subjectId
é obrigatório? Isso ocorre porque a subjectId
é a única maneira de identificar a qual problema de qual repositório é preciso reagir.
É por isso que começamos este exemplo com uma consulta: para obter a ID
.
Vamos examinar a consulta linha por linha:
-
query FindIssueID {
Aqui, estamos executando uma consulta e a nomeamos
FindIssueID
. Observe que nomear uma consulta é opcional; nós damos um nome aqui para que possamos incluí-la na mesma janela do explorador que a mutação. -
repository(owner:"octocat", name:"Hello-World") {
Especificamos o repositório consultando o objeto
repository
e transmitindo os argumentosowner
ename
. -
issue(number:349) {
Especificamos o problema ao qual é preciso reagir consultando o objeto
issue
e transmitindo um argumentonumber
. -
id
É nesse ponto que recuperamos a
id
dehttps://github.com/octocat/Hello-World/issues/349
para transmiti-la como asubjectId
.
Quando executamos a consulta, obtemos a id
: MDU6SXNzdWUyMzEzOTE1NTE=
Observação: a id
retornada na consulta é o valor que transmitiremos como a subjectID
na mutação. Nem a documentação nem a introspecção do esquema indicará essa relação; você precisará entender os conceitos por trás dos nomes para descobrir isso.
Com a identificação conhecida, podemos prosseguir com a mutação:
-
mutation AddReactionToIssue {
Aqui, estamos executando uma mutação e a nomeamos
AddReactionToIssue
. Tal como nas consultas, nomear uma mutação é opcional; aqui, damos um nome para que possamos incluí-la na mesma janela do explorador que a consulta. -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
Vamos examinar essa linha:
addReaction
é o nome da mutação.input
é a chave de argumento obrigatória. Isso sempre seráinput
para uma mutação.{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}
é o valor de argumento obrigatório. Isso sempre será um objeto de entrada (daí as chaves) composto por campos de entrada (subjectId
econtent
, neste caso) para uma mutação.
Como sabemos qual o valor usar para o conteúdo? A documentação de
addReaction
nos informa que o campocontent
tem o tipoReactionContent
, que é uma enumeração, porque só há suporte para algumas reações com emojis nos problemas do GitHub. Estes são os valores permitidos para reações (observe que alguns valores diferem de seus nomes de emojis correspondentes):Conteúdo Emoji +1
👍 -1
👎 laugh
😄 confused
😕 heart
❤️ hooray
🎉 rocket
🚀 eyes
👀 -
O resto da chamada é composto pelo objeto da carga. Aqui é onde especificamos os dados que desejamos que o servidor retorne depois de termos efetuado a mutação. Essas linhas são provenientes da documentação de
addReaction
, que são três campos de retorno possíveis:clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
Neste exemplo, retornamos os dois campos obrigatórios (
reaction
esubject
), ambos com subcampos obrigatórios (content
eid
, respectivamente).
Ao executarmos a mutação, esta é a resposta:
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
É isso! Confira sua reação ao problema posicionando o cursor sobre o 🎉 para encontrar seu nome de usuário.
Observação final: quando você passa vários campos em um objeto de entrada, a sintaxe pode ficar pesada. Pode ser útil mover os campos para uma variável. Veja como você poderia reescrever a mutação original usando uma variável:
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
Você poderá observar que o valor do campo content
no exemplo anterior (em que ele é usado diretamente na mutação) não tem aspas em torno de HOORAY
, mas tem aspas quando usado na variável. Há um motivo para isso:
- Quando você usa
content
diretamente na mutação, o esquema espera que o valor seja do tipoReactionContent
, que é uma enumeração, não uma cadeia de caracteres. A validação de esquema causará um erro se você adicionar aspas no valor do enum, já que as aspas são reservadas para strings. - Ao usar
content
em uma variável, a seção de variáveis precisa ser um JSON válido. Portanto, as aspas são obrigatórias. A validação de esquema interpreta corretamente o tipoReactionContent
quando a variável é transmitida para a mutação durante a execução.
Para obter mais informações sobre a diferença entre enumerações e cadeias de caracteres, confira a especificação oficial do GraphQL.
Leitura adicional
Há muito mais que você pode fazer ao formar chamadas do GraphQL. Aqui estão alguns lugares para procurar a seguir: