Autenticarse con GraphQL
Para comunicarte con el servidor de GraphQL, deberás tener un token OAuth con el alcance correcto.
Sigue los pasos en "Crear un token de acceso personal" para crear un token. Los alcances que requieres dependen del tipo de datos que quieras solicitar. Por ejemplo, selecciona los alcances del Usuario para solicitar datos de usuario. Si necesitas acceder a la información de un repositorio, selecciona los alcances de Repositorio adecuados.
Se recomiendan los siguientes alcances:
user
public_repo
repo
repo_deployment
repo:status
read:repo_hook
read:org
read:public_key
read:gpg_key
La API te notifica si algún recurso requiere de un alcance específico.
Terminal de GraphQL
La API de REST tiene varias terminales; la API de GraphQL solo tiene una terminal:
http(s)://[hostname]/api/graphql
La terminal permanece constante sin importar la operación que realices.
Comunicarse con GraphQL
Ya que las operaciones de GraphQL consisten en JSON de línea múltiple, GitHub te recomienda utilizar el Explorador para hacer llamados de GraphQL. También puedes utilizar cURL o cualquier otra biblioteca que entienda HTTP.
En REST, Los verbos HTTP determinan la operación realizada. En GraphQL, proporcionarás un cuerpo codificado con JSON ya sea que realices una consulta o una mutación, para que el verbo HTTP sea POST
. La excepción es una consulta de introspección, lo cual es un simple GET
en la terminal. Para obtener más información sobre GraphQL contra REST, consulta la sección "Migrarse desde REST a GraphQL".
Para consultar GraphQL utilizando cURL, realiza una solicitud de POST
con una carga útil de JSON. La carga útil deberá contener una cadena llamada query
:
curl -H "Authorization: bearer token" -X POST -d " \
{ \
\"query\": \"query { viewer { login }}\" \
} \
" http(s)://[hostname]/api/graphql
Nota: El valor de cadena de "query"
deve escaoar caracteres de nueva línea o el modelo no lo analizará correctamente. Para el cuerpo POST
, utiliza comillas dobles externas y comillas dobles internas escapadas.
Acerca de las operaciones de consulta y mutación
Los dos tipos de operación permitidos en la API de GraphQL de GitHub son consultas y mutaciones. Comparando GraphQL con REST, las consultas operan como solicitudes de tipo GET
, mientras que las mutaciónes operan como POST
/PATCH
/DELETE
. El nombre de la mutación determina qué modificación se llevará a cabo.
Para obtener información acerca de la limitación de tasas, consulta la sección "Limitaciones de recursos para GraphQL".
Las consultas y mutaciones comparten formatos similares con algunas diferencias importantes.
Acerca de las consultas
Las consultas de GraphQL devuelven únicamente datos que especificas. Para formar una consulta, debes especificar campos dentro de campos (tambien conocidos como subcampos anidados) hasta que te devuelva únicamente escalares.
Las consultas se estructuran de la siguiente forma:
query { JSON objects to return }
Para ver un ejemplo de uso real, consulta "Ejemplo de consulta".
Acerca de las mutaciones
Para formar una mutación, debes especificar tres cosas:
- Nombre de la mutación. El Tipo de modificación que quieres realizar.
- Objeto de entrada. Los datos que quieres enviar al servidor, compuestos de campos de entrada. Pásalo como un argumento al nombre de la mutación.
- Objeto de la carga útil. Los datos que quieres retribuir del servidor, compuestos de campos de devolución. Pásalos como el cuerpo del nombre de la mutación.
Las mutaciones se estructuran de la siguiente forma:
mutation { mutationName(input: {MutationNameInput!}) { MutationNamePayload }
El objeto de entrada en este ejemplo es MutationNameInput
, y la carga útil del objeto es MutationNamePayload
.
En la referencia de mutaciones, los campos de entrada listados son los que quieres pasar como el objeto de entrada. Los campos de devolución son lo que pasas como el objeto de carga útil.
Para ver un ejemplo de uso real, consulta "Ejemplo de mutación".
Trabajar con variables
Las variables pueden conformar consultas más dinámicas y poderosas, y pueden reducir la complejidad cuando pasas objetos de entrada de las mutaciones.
Nota: si estás utilizando el explorador, asegúrate de ingresar las variables en el Panel de Variables de Consulta, y no incluyas la palabra variables
antes del objeto JSON.
Aquí hay una consulta de ejemplo con una sola variable:
query($number_of_repos:Int!) {
viewer {
name
repositories(last: $number_of_repos) {
nodes {
name
}
}
}
}
variables {
"number_of_repos": 3
}
Hay tres pasos para utilizar las variables:
-
Definir la variable fuera de la operación en un objeto
variables
:variables { "number_of_repos": 3 }
El objeto debe ser un JSON válido. Este ejemplo muestra una variable de tipo
Int
, pero es posible definir tipos de variable más complejos, tales como los objetos de entrada. También puedes definir variables múltiples aquí. -
Pasa la variable a la operación como un argumento:
query($number_of_repos:Int!){
El argumento es un par de valor-clave, en donde la clave es el nombre que comienza con
$
(por ejemplo,$number_of_repos
), y el valor es el tipo (por ejemplo,Int
). Agrega un!
para indicar si se requiere el tipo. Si has identificado variables múltiples, inclúyelas aquí como argumentos múltiples. -
Utiliza la variable dentro de la operación:
repositories(last: $number_of_repos) {
En este ejemplo, sustituimos la variable por la cantidad de repositorios a devolver. Especificamos un tipo en el paso 2, ya que GraphQL requiere de una escritura inflexible.
Este proceso hace dinámico al argumento de la consulta. Ahora podemos simplemente cambiar el valor en el objeto variables
y mantener el resto del query tal cual.
Utilizar variables como argumentos te permite actualizar los valores en el objeto variables
dinámicamente sin cambiar la consulta.
Consulta de ejemplo
Analicemos una consulta más compleja y pongamos esta información en contexto.
La siguiente consulta busca el repositorio octocat/Hello-World
, encuentra los 20 informes de problemas más recientes que se han cerrado, y devuelve el título de cada informe de problemas, la URL, y las primeras 5 etiquetas:
query {
repository(owner:"octocat", name:"Hello-World") {
issues(last:20, states:CLOSED) {
edges {
node {
title
url
labels(first:5) {
edges {
node {
name
}
}
}
}
}
}
}
}
Analizando la composición línea por línea:
-
consulta {
Ya que queremos leer los dtos del servidor, y no modoficarlo,
query
es la operación raíz. (si no especificas una operación,query
también es la operación predeterimanda). -
repository(owner:"octocat", name:"Hello-World") {
Para iniciar la consulta, queremos encontrar un objeto
repository
. La validación del modelo indica que este objeto requiere un argumentoowner
yname
. -
issues(last:20, states:CLOSED) {
Para explicar que se buscan todos los informes de problemas en el repositorio, llamamos al objeto
issues
. (Podríamos consultar un soloissue
en unrepository
, pero eso necesitaría que sepamos el número del informe de problemas que queremos se devuelva y proporcionarlo como argumento).Algunos detalles acerca del objeto
issues
:- Los docs nos dicen que este objeto es del tipo
IssueConnection
. - La validación del modelo indica que este objeto requiere de una cantidad de resultados
last
ofirst
como un argumento, así que proporcionamos20
. - Los docs también nos dicen que este objeto acepta un argumento
sttes
, el cual es un enumeradorIssueState
que acepta valores deOPEN
oCLOSED
. Para encontrar únicamente los informes de problemas cerrados, le damos a la clavestates
un valor deCLOSED
.
- Los docs nos dicen que este objeto es del tipo
-
edges {
Sabemos que
issues
es una conexión, ya que tiene el tipoIssueConnection
. Para devolver datos acerca de los informes de problemas individuales, tenemos que acceder al nodo a través deedges
. -
node {
Aquí devolvemos el nodo al final del borde. Los docs
IssueConnection
indican que el nodo al final del tipoIssueConnection
es un objetoIssue
. -
Ahora que sabemos que estamos recuperando un objeto
Issue
, podemos ver al docs y especificar los campos que queremos recuperar:title url labels(first:5) { edges { node { name } } }
Aquí especificamos los campos
title
,url
, ylabels
del objetoIssue
.El campo
labels
tiene el tipoLabelConnection
. Así como el con el objetoissues
, ya quelabels
es una conexión, debemos navegar por sus bordes hacia un nodo conectado: el objetolabel
. En el nodo, podemos especificar los campos del objetolabel
que intentamos recuperar, en este caso,name
.
Notarás que ejecutar esta consulta en el repositorio público Hello-World
de Octocat no recuperará muchas etiquetas. Intenta ejecutarlo en uno de tus propios repositorios que utilice etiquetas, y seguramente verás la diferencia.
Mutación de ejemplo
Las mutaciones a menudo requieren información que solo puedes encontrar si realizas una consulta primero. Este ejemplo muestra dos operaciones:
- Una consulta para obtener la ID de un informe de problemas.
- Una mutación para agregar una reacción de emoji a dicho informe.
query FindIssueID {
repository(owner:"octocat", name:"Hello-World") {
issue(number:349) {
id
}
}
}
mutation AddReactionToIssue {
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
reaction {
content
}
subject {
id
}
}
}
Aunque puedes incluir una consulta y una mutación en la misma ventana del explorador si le das nombres ( en este ejemplo, FindIssueID
y AddReactionToIssue
), las operaciones se ejecutará como llamados separados a la terminal de GraphQL. No se puede realizar una consulta al mismo tiempo que una mutación, o viceversa.
Analicemos el ejemplo. La tarea parece simple: agregar una reacción de emoji a un informe de problemas.
Así que, ¿qué es lo que sabemos para comenzar con la consulta? Aún no sabemos nada.
Ya que queremos modificar los datos en el servidor (agregar un emoji a un informe de problemas), comenzamos buscando el modelo para una mutación útil. Los docs de referencia muestran la mutación addReaction
con la descripción: Adds a reaction to a subject.
. ¡Perfecto!
Los documentos para la mutación listan tres campos de entrada:
clientMutationId
(String
)subjectId
(ID!
)content
(ReactionContent!
)
Los signos !
indican que subjectId
y content
son campos requeridos. Hace sentido que se requiera de un content
: queremos agregar una reacción, así que necesitaremos especificar qué emoji se utilizará.
Pero, ¿por qué se requiere la subjectId
? Esto es porque subjectId
es la única manera de identificar a cuál informe de problemas en cuál repositorio se reaccionará.
Es por esto que comenzamos el ejemplo con una consulta: para obtener la ID
.
Examinemos la consulta línea por línea:
-
query FindIssueID {
Aquí estamos realizando una consulta, y la nombramos
FindIssueID
. Nota que el nombrar una consulta es opcional; le dimos un nombre para que podamos incluirlo en la misma ventana del explorador que utiliza la mutación. -
repository(owner:"octocat", name:"Hello-World") {
Especificamos el repositorio consultando el objeto
repository
y pasando los argumentosowner
yname
. -
issue(number:349) {
Especificamos el informe de problemas al cual se reaccionará consultando el objeto
issue
y pasando un argumentonumber
. -
id
Aquí es donde recuperamos la
id
dehttps://github.com/octocat/Hello-World/issues/349
para pasar como lasubjectId
.
Cuando ejecutamos la consulta, obtenemos la id
: MDU6SXNzdWUyMzEzOTE1NTE=
Nota: La id
que se devuelve en la consulta es el valor que pasaremos como la subjectID
en la mutación. Ni los docs ni la introspección de modelo indicarán esta relación; necesitarás entender los conceptos detrás de los nombres para averiguarla.
Una vez conociendo la ID, podemos proceder con la mutación:
-
mutation AddReactionToIssue {
Aquí realizamos una mutación, y la nombramos
AddReactionToIssue
. Como con las consultas, nombrar una mutación es opcional; le dimos un nombre para poder incluirlo en la misma ventana del explorador que la consulta. -
addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
Examinemos esta línea:
- `addReaction` es el nombre de la mutación. - `input` es la clave de argumento requerida. Esto siempre será la `input` para una mutación. - `{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}` es el valor requerido del argumento. Esto siempre será un [objeto de entrada](/v4/input_object/) (de ahí las corcheas) compuesto de campos de entrada (`subjectId` y `content` en este caso) para una mutación.
¿Cómo sabemos qué valor utilizar para el contenido? Los docs
addReaction
nos dicen que el campocontent
es de tipoReactionContent
, lo cual es un enumerador ya que solo ciertas reacciones de emoji son compatibles con los informes de problemas de GitHub. Estos son los valores permitidos para las reacciones (nota que algunos valores son diferentes de sus nombres de emoji correspondientes):contenido emoji +1
👍 -1
👎 risa
😄 confundido
😕 corazón
❤️ viva
🎉 cohete
🚀 ojos
👀 -
El resto del llamado se compone del objeto de carga útil. Aquí es donde especificamos los datos que queremos recuperar del servidor después de que realicemos la mutación. Estas líneas vienen de los docs
AddReaction
, que tienen tres campos de recuperación posibles:clientMutationId
(String
)reaction
(Reaction!
)subject
(Reactable!
)
En este ejemplo, recuperamos los dos campos requeridos (
reaction
ysubject
), ambos de los cuales tienen subcampos requeridos (respectivamente,content
yid
).
Cuando ejecutamos la mutación, esta es la respuesta:
{
"data": {
"addReaction": {
"reaction": {
"content": "HOORAY"
},
"subject": {
"id": "MDU6SXNzdWUyMTc5NTQ0OTc="
}
}
}
}
¡Listo! Revisa tu reacción al informe de problemas pasando el puntero del mouse sobre 🎉 para encontrar tu nombre de usuario.
Una última nota: cuando pasas varios campos en un objeto de entrada, la sintaxis puede ser difícil de manejar. Mover los campos hacia una variable puede ayudar. Así es como podrías reescribir la mutación original utilizando una variable:
mutation($myVar:AddReactionInput!) {
addReaction(input:$myVar) {
reaction {
content
}
subject {
id
}
}
}
variables {
"myVar": {
"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
"content":"HOORAY"
}
}
Podrás notar que el valor de campo content
en el ejemplo pasado (en donde se usa directamente en la mutación) no tiene comillas encerrando a HOORAY
, pero sí las tiene cuando se incluye en la variable. Esto es por una razón:
- Cuando utlilzas
content
directamente en la mutación, el modelo espera que el valor sea de tipoReactionContent
, lo cual es un enumerador, no una cadena. La validación del modelo arrojará un error si agregas comillas antes y después del valor de enumerador, ya que éstas están reservadas para las cadenas. - Cuando utilizas
content
en una variable, la sección de variables debe ser un JSON válido, así que las comillas son necesarias. La validación del modelo interpreta correctamente el tipoReactionContent
cuando se pasa la variable a la mutación durante la ejecución.
Para obtener más información acerca de la diferencia entre enumeradores y cadenas, consulta official GraphQL spec.
Leer más
Puedes hacer mucho más cuando conformes llamados de GraphQL. Aquí hay algunos lugares que te pueden interesar posteriormente: