Skip to main content

Формирование вызовов с помощью GraphQL

Узнайте, как выполнить проверку подлинности в API GraphQL, а затем узнайте, как создавать и выполнять запросы и изменения.

Проверка подлинности с помощью GraphQL

Вы можете пройти проверку подлинности в API GraphQL с помощью personal access token, GitHub Appили OAuth app.

Проверка подлинности с помощью personal access token

Чтобы выполнить проверку подлинности с помощью personal access token, выполните действия, описанные в разделеУправление личными маркерами доступа". Запрашиваемые данные определяют, какие область или разрешения , вам потребуется.

Например, выберите разрешение "issues:read", чтобы прочитать все проблемы в репозиториях, к которым имеется доступ маркера.

Все данные fine-grained personal access tokens включают доступ на чтение к общедоступным репозиториям. Чтобы получить доступ к общедоступным репозиториям с помощью personal access token (classic), выберите область "public_repo".

Если у маркера нет необходимых область или разрешений для доступа к ресурсу, API вернет сообщение об ошибке, которое указывает на область s или разрешения .

Проверка подлинности с помощью GitHub App

Если вы хотите использовать API от имени организации или другого пользователя, GitHub рекомендует использовать GitHub App. Чтобы атрибутировать действие для приложения, можно выполнить проверку подлинности приложения в качестве установки приложения. Чтобы атрибутировать действие приложения пользователю, можно выполнить проверку подлинности приложения от имени пользователя. В обоих случаях вы создайте маркер, который можно использовать для проверки подлинности в API GraphQL. Дополнительные сведения см. в разделе "[AUTOTITLE" и "Регистрация приложения GitHub](/apps/creating-github-apps/authenticating-with-a-github-app/about-authentication-with-a-github-app)".

Проверка подлинности с помощью OAuth app

Чтобы выполнить проверку подлинности с помощью маркера OAuth из OAuth app, необходимо сначала авторизовать OAuth app с помощью потока веб-приложения или потока устройства. Затем можно использовать маркер доступа, полученный для доступа к API. Дополнительные сведения см. в разделе "[AUTOTITLE" и "Создание приложения OAuth](/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps)".

Конечная точка GraphQL

REST API имеет множество конечных точек; API GraphQL — только одну:

https://api.github.com/graphql

Конечная точка остается неизменной независимо от выполняемой операции.

Взаимодействие с GraphQL

Так как операции GraphQL состоят из нескольких строк кода JSON, GitHub рекомендует использовать Explorer для выполнения вызовов GraphQL. Вы также можете использовать curl или любую другую библиотеку HTTP-речи.

В REST HTTP-команды определяют выполняемую операцию. В GraphQL вы предоставите текст в кодировке JSON независимо от того, выполняете ли вы запрос или изменение, поэтому HTTP-команда — POST. Исключением является запрос интроспекции, который представляет собой простой запрос GET к конечной точке. Дополнительные сведения о GraphQL и REST см. в разделе "Миграция из REST в GraphQL".

Чтобы запросить GraphQL в команде curl , выполните POST запрос с полезными данными JSON. Полезные данные должны содержать строку query:

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

Примечание. В строковом значении "query" символы новой строки должны экранироваться, иначе оно будет неправильно проанализировано схемой. Для текста запроса POST используйте внешние двойные кавычки и экранированные внутренние двойные кавычки.

Операции запроса и изменения

В API GraphQL на GitHub разрешены два типа операций: запросы и изменения. Если сравнивать GraphQL с REST, запросы работают как запросы GET, а изменения — как POST/PATCH/DELETE. Имя изменения определяет производимую модификацию.

Сведения об ограничении скорости см. в разделе "Ограничения скорости и ограничения узлов для API GraphQL".

Запросы и изменения похожи по форме за несколькими важными различиями.

Сведения о запросах

Запросы GraphQL возвращают только указанные данные. Для формирования запроса необходимо указать поля внутри полей (также известные как вложенные подполя), пока не будут возвращены только скалярные значения.

Запросы имеют следующую структуру:

query {
  JSON-OBJECT-TO-RETURN
}

Реальный пример см. в разделе Пример запроса.

Сведения об изменениях

Чтобы сформировать изменение, необходимо указать три элемента.

  1. Имя изменения — тип нужного изменения.
  2. Входной объект — данные, которые необходимо отправить на сервер; состоят из полей входных данных. Передайте его в качестве аргумента имени изменения.
  3. Объект полезных данных — данные, которые должны быть получены с сервера; состоят из возвращаемых полей. Передайте его в качестве тела имени мутации.

Изменения имеют следующую структуру:

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

Входной объект в этом примере — MutationNameInput, а объект полезных данных — MutationNamePayload.

В справочнике по изменениям перечислены поля входных данных, передаваемые в качестве входного объекта. Кроме того, перечислены возвращаемые поля, передаваемые в качестве объекта полезных данных.

Реальный пример см. в разделе Пример изменения.

Работа с переменными

Переменные могут сделать запросы более динамичными и эффективными, снизив сложность при передаче входных объектов изменений.

Примечание. Если вы используете Explorer, вводите переменные в отдельной области "Переменные запроса" и не добавляйте слово variables перед объектом JSON.

Ниже приведен пример запроса с одной переменной.

query($number_of_repos:Int!) {
  viewer {
    name
     repositories(last: $number_of_repos) {
       nodes {
         name
       }
     }
   }
}
variables {
   "number_of_repos": 3
}

Переменные используются в три этапа.

  1. Определите переменную вне операции в объекте variables:

    variables {
       "number_of_repos": 3
    }
    

    Объект должен быть допустимым кодом JSON. В этом примере показана переменная простого типа Int, но можно определять переменные и более сложных типов, такие как входные объекты. Здесь также можно определить несколько переменных.

  2. Передайте переменную в операцию в качестве аргумента:

    query($number_of_repos:Int!){
    

    Аргумент представляет собой пару "ключ-значение", где ключ — это имя, начинающееся с $ (например, $number_of_repos), а значение — это тип (например, Int). Чтобы указать, что тип является обязательным, добавьте !. Если вы определили несколько переменных, включите их здесь как несколько аргументов.

  3. Используйте переменную в операции:

    repositories(last: $number_of_repos) {
    

    В этом примере переменная означает количество получаемых репозиториев. Тип указывается на шаге 2, так как в GraphQL действует строгая типизация.

Этот процесс делает аргумент запроса динамическим. Теперь мы можем просто изменить значение в объекте variables, оставив остальную часть запроса без изменений.

Использование переменных в качестве аргументов позволяет динамически обновлять значения в объекте variables, не меняя запрос.

Пример запроса

Давайте рассмотрим более сложный запрос в контексте.

Следующий запрос обращается к репозиторию octocat/Hello-World, находит 20 последних закрытых проблем и возвращает заголовок, URL-адрес и первые пять меток каждой проблемы:

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. (Мы могли бы запросить одну проблему issue из объекта repository, но для этого нужно знать количество возвращаемых проблем и предоставить его в качестве аргумента.)

    Некоторые сведения об объекте 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
        }
      }
    }
    

    В данном случае мы указываем поля title, url и labels объекта Issue.

    Тип поля labels — LabelConnection. Как и в случае с объектом issues, так как labels — это соединение, необходимо пройти по его ребрам к подключенному узлу: объекту label. В этом узле можно указать поля объекта label, которые нужно вернуть, в данном случае name.

Вы можете заметить, что выполнение этого запроса в общедоступном Hello-World репозитории Octocat не возвращает много меток. Попробуйте выполнить его в одном из собственных репозиториев, использующих метки, и вы, скорее всего, заметите разницу.

Пример изменения

Для изменений часто требуются сведения, которые можно узнать только путем предварительного выполнения запроса. В этом примере показаны две операции:

  1. запрос для получения идентификатора проблемы;
  2. изменение для добавления реакции эмодзи на проблему.
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 с таким описанием: 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

    Здесь мы получаем id https://github.com/octocat/Hello-World/issues/349 для передачи в subjectId.

При выполнении запроса мы получаем id MDU6SXNzdWUyMzEzOTE1NTE=.

Примечание. Возвращаемое в запросе значение id — это значение, которое мы передадим как subjectID в изменении. Ни в документации, ни в схеме эта связь не определена. Необходимо просто понимать, как работают имена.

Зная идентификатор, мы можем перейти к изменению:

  • 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 доступно гораздо больше возможностей. Дополнительную информацию можно найти в следующих ресурсах: