Skip to main content

Erstellen von Aufrufen mit GraphQL

Hier erfährst du, wie du dich bei der GraphQL-API authentifizierst und anschließend Abfragen und Mutationen erstellst und ausführst.

Authentifizieren mit GraphQL

Du kannst dich mit einem personal access token, der GitHub App oder der OAuth app bei der GraphQL-API authentifizieren.

Authentifizieren mit einem personal access token

Um dich mit einem personal access token zu authentifizieren, befolge die Schritte unter Verwalten deiner persönlichen Zugriffstoken. Die von dir angeforderten Daten bestimmen, welche Bereiche oder Berechtigungen du benötigst.

Wählen Sie beispielsweise die Berechtigung „issues:read“ aus, um alle Issues in den Repositorys lesen zu können, auf die Ihr Token Zugriff hat.

Alle fine-grained personal access token haben Lesezugriff auf öffentliche Repositorys. Um mit einem personal access token (classic) auf öffentliche Repositorys zuzugreifen, wähle den Bereich „public_repo“ aus.

Wenn dein Token nicht über die erforderlichen Bereiche oder Berechtigungen für den Zugriff auf eine Ressource verfügt, gibt die API eine Fehlermeldung zurück, in der die Bereiche oder Berechtigungen angegeben werden, die dein Token benötigt.

Authentifizieren mit einer GitHub App

Wenn du die API im Namen einer Organisation oder eines anderen Benutzers nutzen möchtest, empfiehlt GitHub, dass du eine GitHub App verwendest. Um die Aktivität deiner App zuzuordnen, kannst du deine App als eine App-Installation authentifizieren lassen. Um App-Aktivitäten einemeiner Benutzerin zuzuordnen, kannst du deine App im Namen eines Benutzers bzw. einer Benutzerin authentifizieren. In beiden Fällen generierst du ein Token, das du zur Authentifizierung bei der GraphQL-API verwenden kannst. Weitere Informationen findest du unter Registrieren einer GitHub-App und unter Informationen zur Authentifizierung mit einer GitHub-App.

Authentifizieren mit OAuth app

Um dich mit einem OAuth-Token in einer OAuth app zu authentifizieren, musst du deine OAuth app entweder mit einem Webanwendungs- oder Geräteflow autorisieren. Anschließend kannst du mit dem erhaltenen Zugriffstoken auf die API zugreifen. Weitere Informationen findest du unter „Erstellen einer OAuth-App“ und „Autorisieren von OAuth-Apps“.

Der GraphQL-Endpunkt

Die REST-API verfügt über zahlreiche Endpunkte; die GraphQL-API verfügt über einen einzelnen Endpunkt:

https://api.github.com/graphql

Der Endpunkt bleibt konstant, unabhängig davon, welchen Vorgang du ausführst.

Kommunizieren mit GraphQL

Da GraphQL-Vorgänge aus Multiline JSON bestehen, empfiehlt GitHub die Verwendung des Explorers, um GraphQL-Aufrufe vorzunehmen. Du kannst auch curl oder eine andere Bibliothek mit HTTP-Unterstützung verwenden.

In REST bestimmen HTTP-Verben den ausgeführten Vorgang. In GraphQL stellst du einen JSON-codierten Textkörper bereit, unabhängig davon, ob du eine Abfrage oder eine Mutation ausführst; daher lautet das HTTP-Verb POST. Die Ausnahme ist eine Introspektionsabfrage, die für den Endpunkt einfach GET ist. Weitere Informationen zu GraphQL und REST findest du unter Migrieren von REST zu GraphQL.

Zur Abfrage von GraphQL mit einem curl-Befehl sendest du eine POST-Anforderung mit JSON-Nutzdaten. Die Nutzlast muss eine Zeichenfolge namens query enthalten:

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

Hinweis: Der Zeichenfolgenwert von "query" muss für Neue-Zeile-Zeichen ein Escapezeichen verwenden, anderenfalls wird das Schema nicht ordnungsgemäß analysiert. Verwende für den POST-Text äußere doppelte Anführungszeichen und innere doppelte Anführungszeichen mit Escapezeichen.

Informationen zu Abfrage- und Mutationsvorgängen

Die beiden Typen zulässiger Vorgänge in der GraphQL-API von GitHub sind Abfragen und Mutationen. Wenn GraphQL mit REST verglichen wird, funktionieren Abfragen wie GET-Anforderungen, während Mutationen wie POST/PATCH/DELETE funktionieren. Der Mutationsname bestimmt, welche Änderung ausgeführt wird.

Weitere Informationen zur Ratenbegrenzung findest du unter Ratenbegrenzungen und Knotengrenzwerte für die GraphQL-API.

Abfragen und Mutationen teilen ähnliche Formen, mit einigen wichtigen Unterschieden.

Informationen zu Abfragen

GraphQL-Abfragen geben nur die von dir angegebenen Daten zurück. Beim Erstellen einer Abfrage musst du Felder in Feldern (auch als geschachtelte Unterfelder bezeichnet) angeben, bis nur noch Skalare zurückgegeben werden.

Abfragen sind wie folgt strukturiert:

query {
  JSON-OBJECT-TO-RETURN
}

Ein reales Beispiel findest du unter Beispielabfrage.

Informationen zu Mutationen

Zum Erstellen einer Mutation musst du drei Dinge angeben:

  1. Mutationsname. Die Art der Änderung, die du ausführen möchtest.
  2. Eingabeobjekt. Die Daten, die du an den Server senden möchtest, bestehen aus Eingabefeldern. Übergib es als Argument an den Mutationsnamen.
  3. Nutzlastobjekt. Die Daten, die du vom Server zurückgeben möchtest; sie bestehen aus Rückgabefeldern. Übergib sie als Körper des Mutationsnamens.

Mutationen sind wie folgt strukturiert:

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

Das Eingabeobjekt in diesem Beispiel lautet MutationNameInput, und das Nutzlastobjekt ist MutationNamePayload.

In der Mutationsreferenz sind die aufgeführten Eingabefelder das, was du als Eingabeobjekt übergibst. Die aufgeführten Rückgabefelder sind das, was du als Nutzlastobjekt übergibst.

Ein reales Beispiel findest du unter Beispielmutation.

Arbeiten mit Variablen

Variablen können Abfragen dynamischer und leistungsfähiger machen, und sie können die Komplexität beim Übergeben von Mutationseingabeobjekten verringern.

Hinweis: Wenn du den Explorer verwendest, musst du Variablen im separaten Abfragevariablenbereich eingeben. Schließe nicht das Wort variables vor dem JSON-Objekt ein.

Nachfolgend findest du eine Beispielabfrage mit einer einzelnen Variable:

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

Es gibt drei Schritte zum Verwenden von Variablen:

  1. Definiere die Variable außerhalb des Vorgangs in einem variables-Objekt:

    variables {
       "number_of_repos": 3
    }
    

    Das Objekt muss gültiger JSON-Code sein. In diesem Beispiel wird ein einfacher Int-Variablentyp dargestellt, aber es ist möglich, komplexere Variablentypen wie Eingabeobjekte zu definieren. Du kannst hier auch mehrere Variablen definieren.

  2. Übergib die Variable als Argument an den Vorgang:

    query($number_of_repos:Int!){
    

    Das Argument ist ein Schlüssel-Wert-Paar, wobei der Schlüssel der Name ist, der mit $ beginnt (z. B. $number_of_repos), und der Wert der Typ (z. B. Int). Gib zusätzlich mit ! an, ob der Typ erforderlich ist. Wenn du mehrere Variablen definiert hast, füge sie hier als mehrere Argumente ein.

  3. Verwende die Variable innerhalb des Vorgangs:

    repositories(last: $number_of_repos) {
    

    In diesem Beispiel ersetzen wir die Variable für die Anzahl der Repositorys, die abgerufen werden sollen. Wir geben einen Typ in Schritt 2 an, da GraphQL strenge Typisierung erzwingt.

Dieser Prozess macht das Abfrageargument dynamisch. Wir können nun einfach den Wert im variables-Objekt ändern und den Rest der Abfrage unverändert lassen.

Mithilfe von Variablen als Argumente kannst du Werte im variables-Objekt dynamisch aktualisieren, ohne die Abfrage zu ändern.

Beispielabfrage

Gehe die folgende komplexere Abfrage durch, die diese Informationen in einen Kontext einbindet.

Mit ihr wird das Repository octocat/Hello-World gesucht, und sie gibt die 20 zuletzt geschlossenen Issues und jeweils den Titel, die URL und die ersten 5 Bezeichnungen zurück:

query {
  repository(owner:"octocat", name:"Hello-World") {
    issues(last:20, states:CLOSED) {
      edges {
        node {
          title
          url
          labels(first:5) {
            edges {
              node {
                name
              }
            }
          }
        }
      }
    }
  }
}

Sieh dir Zeile für Zeile an, wie sie zusammengesetzt ist:

  • query {

    Dein Ziel ist es, Daten vom Server zu lesen und nicht zu ändern. Daher lautet der Stammvorgang query. (Wenn du keinen Vorgang angibst, ist query auch die Standardeinstellung.)

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

    Zu Beginn der Abfrage soll das repository-Objekt gesucht werden. Die Schemavalidierung gibt an, dass dieses Objekt einen Besitzer (owner) und ein name-Argument erfordert.

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

    Damit alle Issues im Repository berücksichtigt werden, rufen wir das issues-Objekt auf. (Wir könnten ein einzelnes issue in einem repository abfragen, aber das erfordert, dass wir die Nummer des Issues kennen, das wir zurückgeben möchten, und diese als Argument angeben.)

    Einige Details zum issues-Objekt:

    • Die Dokumentation gibt an, dass dieses Objekt den Typ IssueConnectionaufweist.
    • Die Schemavalidierung gibt an, dass dieses Objekt eine letzte (last) oder erste (first) Nummer der Ergebnisse als Argument erfordert. Daher geben wir 20 an.
    • Die Dokumentation gibt auch an, dass dieses Objekt ein states-Argument akzeptiert, das eine IssueState-Aufzählung ist, die OPEN- oder CLOSED-Werte akzeptiert. Damit nur geschlossene Issues gesucht werden, erhält der Schlüssel states den Wert CLOSED.
  • edges {

    Wir wissen, dass issues eine Verbindung ist, da es den Typ IssueConnection aufweist. Zum Abrufen von Daten zu einzelnen Issues müssen wir über edges auf den Knoten zugreifen.

  • node {

    Hier wird der Knoten am Ende des Edges abgerufen. Die IssueConnection-Dokumentation gibt an, dass der Knoten am Ende des IssueConnection-Typs ein Issue-Objekt ist.

  • Nachdem nun bekannt ist, dass wir ein Issue-Objekt abrufen, können wir anhand der Dokumentation die Felder angeben, die zurückgegeben werden sollen:

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

    Hier geben wir die Felder title, url und labels des Issue-Objekts an.

    Das Feld labels besitzt den Typ LabelConnection. Da labels eine Verbindung ist, müssen wir wie beim issues-Objekt über die Edges der Verbindung zu einem verbundenen Knoten gelangen, dem label-Objekt. Auf dem Knoten können wir die label-Objektfelder angeben, die zurückgegeben werden sollen, in diesem Fall name.

Du stellst möglicherweise fest, dass die Ausführung dieser Abfrage für das öffentliche Hello-WorldOctocat-Repository nicht viele Bezeichnungen zurückgibt. Versuche, sie auf einem deiner eigenen Repositorys auszuführen, die Bezeichnungen verwenden. Dann wirst du wahrscheinlich einen Unterschied sehen.

Beispielmutation

Mutationen erfordern häufig Informationen, die du nur ermitteln kannst, indem du zuerst eine Abfrage ausführst. In diesem Beispiel werden zwei Vorgänge gezeigt:

  1. Eine Abfrage zum Abrufen einer Issue-ID.
  2. Eine Mutation, um dem Issue eine Emoji-Reaktion hinzuzufügen.
query FindIssueID {
  repository(owner:"octocat", name:"Hello-World") {
    issue(number:349) {
      id
    }
  }
}

mutation AddReactionToIssue {
  addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}

Obwohl du eine Abfrage und eine Mutation in das gleiche Explorer-Fenster aufnehmen kannst, wenn du ihnen Namen gibst (in diesem Beispiel FindIssueID und AddReactionToIssue), werden die Vorgänge als separate Aufrufe des GraphQL-Endpunkts ausgeführt. Es ist nicht möglich, eine Abfrage gleichzeitig mit einer Mutation auszuführen.

Gehen wir das Beispiel durch. Die Aufgabe klingt einfach: Füge einem Issue eine Emoji-Reaktion hinzu.

Wie wissen wir also, das mit einer Abfrage begonnen werden soll? Wir wissen es noch nicht.

Da wir Daten auf dem Server ändern möchten (ein Emoji an ein Issue anfügen), durchsuchen wir zu Beginn das Schema nach einer hilfreichen Mutation. Die Referenzdokumentation gibt die addReaction-Mutation mit dieser Beschreibung an: Adds a reaction to a subject. Perfekt!

Die Dokumentation für die Mutation listet drei Eingabefelder auf:

  • clientMutationId (String)
  • subjectId (ID!)
  • content (ReactionContent!)

Mit ! wird angegeben, dass subjectId und content erforderliche Felder sind. Es ist verständlich, dass content erforderlich ist: Wir möchten eine Reaktion hinzufügen und müssen daher angeben, welches Emoji verwendet werden soll.

Aber warum ist subjectId erforderlich? Es liegt daran, dass nur mit subjectId bestimmt werden kann, auf welches Issue in welchem Repository reagiert werden soll.

Aus diesem Grund beginnen wir dieses Beispiel mit einer Abfrage: um die ID abzurufen.

Sehen wir uns nun die Abfrage Zeile für Zeile an:

  • query FindIssueID {

    Hier führen wir eine Abfrage aus und nennen sie FindIssueID. Beachte, dass die Benennung einer Abfrage optional ist; hier erhält sie einen Namen, damit wir sie in das gleiche Explorer-Fenster wie die Mutation aufnehmen können.

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

    Wir geben das Repository an, fragen dazu das repository-Objekt ab und übergeben die Argumente owner und name.

  • issue(number:349) {

    Wir geben das Issue an, auf das reagiert werden soll, fragen dazu das issue-Objekt ab und übergeben ein number-Argument.

  • id

    Dies ist der Ort, an dem wir die id von https://github.com/octocat/Hello-World/issues/349 abrufen, um sie als die subjectId zu übergeben.

Wenn wir die Abfrage ausführen, erhalten wir die id: MDU6SXNzdWUyMzEzOTE1NTE=

Hinweis: Die in der Abfrage zurückgegebene id ist der Wert, den wir als subjectID in der Mutation übergeben werden. Weder die Dokumentation noch die Schemaintrospektion geben diese Beziehung an; Du musst die Konzepte hinter den Namen verstehen, um dies zu ermitteln.

Mit der bekannten ID können wir mit der Mutation fortfahren:

  • mutation AddReactionToIssue {

    Hier führen wir eine Mutation aus und nennen sie AddReactionToIssue. Wie bei Abfragen ist die Benennung einer Mutation optional; hier erhält sie einen Namen, damit wir sie in das gleiche Explorer-Fenster wie die Abfrage aufnehmen können.

  • addReaction(input:{subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY}) {

    Sehen wir uns diese Zeile an:

    • addReaction ist der Name der Mutation.
    • input ist der erforderliche Argumentschlüssel. Dies ist für eine Mutation immer input.
    • {subjectId:"MDU6SXNzdWUyMzEzOTE1NTE=",content:HOORAY} ist der erforderliche Argumentwert. Dies ist für eine Mutation immer ein Eingabeobjekt (daher die geschweiften Klammern) aus Eingabefeldern (in diesem Fall subjectId und content).

    Wie wissen wir, welcher Wert für den Inhalt verwendet werden soll? Die addReaction-Dokumentation gibt an, dass das Feld content den Typ ReactionContent aufweist, wobei es sich um eine Enumeration handelt, da nur bestimmte Emoji-Reaktionen bei GitHub-Issues unterstützt werden. Dies sind die zulässigen Werte für Reaktionen (einige Werte weichen von den entsprechenden Emojinamen ab):

    Inhalt Emoji
    +1 👍
    -1 👎
    laugh 😄
    confused 😕
    heart ❤️
    hooray 🎉
    rocket 🚀
    eyes 👀
  • Der Rest des Aufrufs besteht aus dem Nutzlastobjekt. Hier geben wir die Daten an, die vom Server zurückgegeben werden sollen, nachdem die Mutation durchgeführt wurde. Diese Zeilen stammen aus der addReaction-Dokumentation und weisen drei mögliche Rückgabefelder auf:

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

    In diesem Beispiel geben wir die beiden erforderlichen Felder (reaction und subject) zurück, die beide über erforderliche Unterfelder verfügen (content bzw. id).

Wenn wir die Mutation ausführen, ist dies die Reaktion:

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

Das ist alles! Sieh dir die Reaktion auf das Issue an, indem du auf „🎉“ zeigst, um deinen Benutzernamen zu finden.

Ein letzter Hinweis: Wenn du mehrere Felder in einem Eingabeobjekt übergibst, kann die Syntax unübersichtlich werden. Das Verschieben der Felder in eine Variable kann helfen. So kannst du die ursprüngliche Mutation mithilfe einer Variablen neu schreiben:

mutation($myVar:AddReactionInput!) {
  addReaction(input:$myVar) {
    reaction {
      content
    }
    subject {
      id
    }
  }
}
variables {
  "myVar": {
    "subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=",
    "content":"HOORAY"
  }
}

Du kannst feststellen, dass der content-Feldwert im vorherigen Beispiel (in dem er direkt in der Mutation verwendet wird) keine Anführungszeichen um HOORAY enthält; er enthält jedoch Anführungszeichen, wenn er in der Variablen verwendet wird. Hierfür gibt es einen Grund:

  • Wenn du content direkt in der Mutation verwendest, erwartet das Schema für den Wert den Typ ReactionContent, der eine Enumeration ist, keine Zeichenfolge. Die Schemaüberprüfung löst einen Fehler aus, wenn du Anführungszeichen um den Enumerationswert hinzufügst, da Anführungszeichen für Zeichenfolgen reserviert sind.
  • Wenn du content in einer Variablen verwendest, muss der Abschnitt für die Variablen gültiger JSON-Code sein, und daher sind die Anführungszeichen erforderlich. Die Schemaüberprüfung interpretiert den Typ ReactionContent richtig, wenn die Variable während der Ausführung an die Mutation übergeben wird.

Weitere Informationen zum Unterschied zwischen Enumerationen und Zeichenfolgen findest du in der offiziellen GraphQL-Spezifikation.

Weitere Informationsquellen

Beim Erstellen von GraphQL-Aufrufen hast du zahlreiche weitere Möglichkeiten. Weitere Informationen findest du beispielsweise unter den folgenden Themen: