Skip to main content

Formar llamados con GraphQl

Aprende cómo autenticarte en la API de GraphQL, y luego cómo crear y ejecutar consultas y mutaciones.

Autenticarse con GraphQL

Puedes autenticarte en la GraphQL API mediante un personal access token, una GitHub App o una OAuth app.

Autenticación con un personal access token

Si deseas autenticarte con un personal access token, sigue los pasos que se indican en "Administración de tokens de acceso personal". Los datos que solicitas determinarán qué ámbitos o permisos necesitarás.

Por ejemplo, selecciona el permiso "issues:read" para leer todas las incidencias de los repositorios a los que tiene acceso el token.

Todos los fine-grained personal access token incluyen acceso de lectura a repositorios públicos. Para acceder a repositorios públicos con un personal access token (classic), selecciona el ámbito "public_repo".

Si el token no tiene los ámbitoso los permisos necesarios para acceder a un recurso, la API devolverá un mensaje de error que indica los ámbitos o los permisos que el token necesita.

Autenticación con una GitHub App

Si deseas usar la API en nombre de una organización u otro usuario, GitHub recomienda usar un GitHub App. Para atribuir la actividad a la aplicación, puedes hacer que la aplicación se autentique como una instalación de aplicación. Para atribuir la actividad de la aplicación a un usuario, puedes hacer que la aplicación se autentique en nombre de un usuario. En ambos casos, generarás un token que puedes usar para autenticarte en la GraphQL API. Para obtener más información, vea «Registro de una instancia de GitHub App» y «Acerca de la autenticación con una aplicación de GitHub».

Autenticación con OAuth app

Para autenticarte con un token de OAuth desde OAuth app, primero debes autorizar OAuth app mediante un flujo de aplicación web o un flujo de dispositivo. A continuación, puedes usar el token de acceso que recibiste para acceder a la API. Para más información, consulta "Creación de una aplicación de OAuth" y "Autorización de aplicaciones de OAuth".

Terminal de GraphQL

La API de REST tiene varias terminales; la API de GraphQL solo tiene una terminal:

https://api.github.com/graphql

La terminal permanece constante sin importar la operación que realices.

Comunicarse con GraphQL

Como las operaciones de GraphQL constan de código JSON de varias líneas, GitHub recomienda usar el Explorador para realizar llamadas a 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, tendrá que proporcionar un cuerpo codificado con JSON cuando realice una consulta o una mutación, por lo que el verbo HTTP es POST. La excepción es una consulta de introspección, que es una GET sencilla al punto de conexión. Para obtener más información sobre las diferencias entre GraphQL y REST, consulta "Migrar desde Rest hacia GraphQL".

Para consultar GraphQL mediante un comando curl, realiza una solicitud POST con una carga JSON. La carga debe contener una cadena denominada query:

curl -H "Authorization: bearer TOKEN" -X POST -d " \
 { \
   \"query\": \"query { viewer { login }}\" \
 } \
" https://api.github.com/graphql

Nota: El valor de cadena de "query" debe aplicar escape a los caracteres de nueva línea o el esquema no lo analizará correctamente. Para el cuerpo POST, use comillas dobles externas y comillas dobles interiores con escape.

Acerca de las operaciones de consulta y mutación

Los dos tipos de operaciones permitidas en GraphQL API de GitHub son las consultas y las mutaciones. Si se compara GraphQL con REST, las consultas funcionan como solicitudes GET, mientras que las mutaciones funcionan comoPOST/PATCH/DELETE. El nombre de la mutación determina qué modificación se ejecuta.

Para obtener información sobre la limitación de tasas, consulta "Limitaciones de recursos".

Las consultas y mutaciones comparten formatos similares con algunas diferencias importantes.

Acerca de las consultas

Las consultas de GraphQL devuelven solo los datos que especifique. Para crear una consulta, debe especificar campos dentro de campos (también denominados subcampos anidados) hasta que solo se devuelvan valores escalares.

Las consultas se estructuran de esta manera:

query {
  JSON-OBJECT-TO-RETURN
}

Para obtener un ejemplo real, vea "Consulta de ejemplo".

Acerca de las mutaciones

Para formar una mutación, debes especificar tres cosas:

  1. Nombre de la mutación. El Tipo de modificación que quieres realizar.
  2. Objeto de entrada. Los datos que quiere enviar al servidor, formados por campos de entrada. Pásalo como un argumento al nombre de la mutación.
  3. Objeto de carga. Los datos que quiere devolver desde el servidor, formados por campos devueltos. Pásalos como el cuerpo del nombre de la mutación.

Las mutaciones se estructuran de la siguiente forma:

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

El objeto de entrada de este ejemplo es MutationNameInputy el objeto de carga es MutationNamePayload.

En la referencia de mutaciones, los campos de entrada enumerados son lo que se pasa como objeto de entrada. Los campos devueltos enumerados son lo que se pasa como objeto de carga.

Para obtener un ejemplo real, vea "Mutación de ejemplo".

Trabajo con variables

Lasvariables pueden hacer que las consultas sean más dinámicas y eficaces, y pueden reducir la complejidad al pasar objetos de entrada de mutación.

Nota: Si usa el Explorador, asegúrese de escribir variables en el panel Variables de consulta independiente y no incluya 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:

  1. Defina la variable fuera de la operación en un objeto variables:

    variables {
       "number_of_repos": 3
    }
    

    El objeto debe ser un JSON válido. En este ejemplo se muestra un tipo de variable Int sencillo, pero se pueden definir tipos de variable más complejos, como los objetos de entrada. También puedes definir variables múltiples aquí.

  2. Pasa la variable a la operación como un argumento:

    query($number_of_repos:Int!){
    

    El argumento es un par clave-valor, donde la clave es el nombre que empieza por $ (por ejemplo, $number_of_repos) y el valor es el tipo (por ejemplo, Int). Agregue ! para indicar si el tipo es obligatorio. Si has identificado variables múltiples, inclúyelas aquí como argumentos múltiples.

  3. 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 se puede cambiar el valor en el objeto variables y mantener el resto de la consulta igual.

El uso de variables como argumentos permite actualizar los valores del objeto variables de forma dinámica sin cambiar la consulta.

Ejemplo de consulta

Analicemos una consulta más compleja y pongamos esta información en contexto.

La consulta siguiente examina el repositorio octocat/Hello-World, busca las 20 incidencias cerradas más recientes y devuelve el título, la dirección URL y las 5 primeras etiquetas de cada incidencia:

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:

  • query {

    Como el objetivo es leer datos del servidor, no modificarlos, la operación raíz es query. (Si no especifica una operación, query también es el valor predeterminado).

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

    Para comenzar la consulta, se busca un objeto repository. La validación del esquema indica que este objeto necesita un owner y un argumento name.

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

    Para tener en cuenta todas las incidencias del repositorio, se llama al objeto issues. (Se podría consultar un único issue en repository, pero para eso sería necesario conocer el número de la incidencia que se quiere devolver y proporcionarlo como argumento).

    Algunos detalles sobre el objeto issues:

    • En la documentación se indica que este objeto tiene el tipo IssueConnection.
    • La validación del esquema indica que este objeto necesita un número de resultados last o first como argumento, por lo que se proporciona 20.
    • En la documentación también se indica que este objeto acepta un argumento states, que es una enumeración IssueState que acepta valores OPEN o CLOSED. Para busca solo incidencias cerradas, se asigna un valor de CLOSED a la clave states.
  • edges {

    Se sabe que issues es una conexión porque tiene el tipo IssueConnection. Para recuperar datos sobre incidencias individuales, es necesario al nodo por medio de edges.

  • node {

    Aquí devolvemos el nodo al final del borde. En la documentación de IssueConnection se indica que el nodo al final del tipo IssueConnection es un objeto Issue.

  • Ahora que se sabe que se va a recuperar un objeto Issue, se puede examinar la documentación y especificar los campos que se quieren devolver:

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

    Aquí se especifican los campos title, url y labels del objeto Issue.

    El campo labels tiene el tipo LabelConnection. Como sucede con el objeto issues, como labels es una conexión, es necesario desplazar sus bordes a un nodo conectado: el objeto label. En el nodo, se pueden especificar los campos del objeto label que se quieren devolver, en este caso, name.

Es posible que observe que la ejecución de esta consulta en el repositorio Hello-World público de Octocat no devuelve 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:

  1. Una consulta para obtener la ID de un informe de problemas.
  2. 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 puede incluir una consulta y una mutación en la misma ventana del Explorador si les asigna nombres (FindIssueID y AddReactionToIssue en este ejemplo), las operaciones se ejecutarán como llamadas independientes al punto de conexión 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. En la documentación de referencia se muestra la mutación addReaction, con esta 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 valores ! indican que subjectId y content son campos obligatorios. Un campo content obligatorio tiene sentido: el objetivo es agregar una reacción, por lo que será necesario especificar el emoji que se va a usar.

¿Pero por qué subjectId es obligatorio? Se debe a que subjectId es la única manera de identificar a qué incidencia de qué repositorio se debe reaccionar.

Este es el motivo de comenzar el ejemplo con una consulta: para obtener ID.

Examinemos la consulta línea por línea:

  • query FindIssueID {

    Aquí se realiza una consulta y se le asigna el nombre 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") {

    Para especificar el repositorio se consulta el objeto repository y se pasan los argumentos owner y name.

  • issue(number:349) {

    Para especificar la incidencia a la que reaccionar se consulta el objeto issue y se pasa un argumento number.

  • id

    Aquí es donde se recupera el valor id de https://github.com/octocat/Hello-World/issues/349 para pasarlo como subjectId.

Cuando se ejecuta la consulta, se obtiene id: MDU6SXNzdWUyMzEzOTE1NTE=.

Nota: El valor id devuelto en la consulta es el que se pasará como 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í se ejecuta una mutación y se le asigna el nombre 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 obligatoria. Para una mutación siempre será input.
    • {subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY} es la valor de argumento obligatorio. Siempre será un objeto de entrada (de ahí las llaves) formado por campos de entrada (subjectId y content en este caso) para una mutación.

    ¿Cómo sabemos qué valor utilizar para el contenido? En la documentación de addReaction se indica que el campo content tiene el tipo ReactionContent, que es una enumeración porque en las incidencias de GitHub solo se admiten ciertas de reacciones emoji. Estos son los valores permitidos para las reacciones (nota que algunos valores son diferentes de sus nombres de emoji correspondientes):

    Contenido Emoji
    +1 👍
    -1 👎
    laugh 😄
    confused 😕
    heart ❤️
    hooray 🎉
    rocket 🚀
    eyes 👀
  • 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 proceden de la documentación de addReaction, con tres campos devueltos posibles:

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

    En este ejemplo, se devuelven los dos campos obligatorios (reaction y subject), que tienen subcampos obligatorios (content y id, respectivamente).

Cuando ejecutamos la mutación, esta es la respuesta:

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

Eso es todo. Para consultar la reacción a la incidencia mantenga el mouse sobre 🎉 para encontrar el nombre de usuario.

Una última nota: cuando pasas varios campos en un objeto de entrada, la sintaxis puede ser difícil de manejar. Puede resultar útil mover los campos a una variable. 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"
  }
}

Es posible que observe que el valor del campo content en el ejemplo anterior (donde se usa directamente en la mutación) no tiene comillas alrededor de HOORAY, pero sí cuando se usa en la variable. Esto es por una razón:

  • Cuando se usa content directamente en la mutación, el esquema espera que el valor sea de tipo ReactionContent, que es una enumeración, 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 se usa content en una variable, la sección de variables debe ser código JSON válido, por lo que las comillas son obligatorias. La validación del esquema interpreta correctamente el tipo ReactionContent cuando la variable se pasa a la mutación durante la ejecución.

Para más información sobre la diferencia entre enumeraciones y cadenas, vea la especificación oficial de GraphQL.

Información adicional

Hay mucho más que puede hacer al realizar llamadas a GraphQL. Aquí hay algunos lugares que te pueden interesar posteriormente: