Was ist GraphQL?

Überblick

GraphQL ist eine neue und aufregende API für Ad-hoc-Abfragen und -Bearbeitung. Es ist äußerst flexibel und bietet viele Vorteile. Es eignet sich besonders zum Freilegen von Daten, die als Diagramme und Bäume organisiert sind. Facebook hat GraphQL im Jahr 2012 entwickelt und 2015 als Open Source bereitgestellt. 

Sie nahm rasch an und wurde zu einer der heißesten Technologien. Viele innovative Unternehmen haben GraphQL in der Produktion übernommen und verwendet. In diesem Tutorial erfahren Sie: 

  • die Prinzipien von GraphQL
  • wie es mit REST vergleichbar ist
  • wie man Schemata entwirft
  • So richten Sie einen GraphQL-Server ein
  • wie man Abfragen und Mutationen implementiert 
  • und ein paar zusätzliche fortgeschrittene Themen

Wo leuchtet GraphQL??

GraphQL ist am besten, wenn Ihre Daten in einer Hierarchie oder einem Diagramm organisiert sind und das Frontend auf verschiedene Untermengen dieser Hierarchie oder dieses Diagramms zugreifen möchte. Betrachten Sie eine Anwendung, die die NBA verfügbar macht. Du hast Teams, Spieler, Trainer, Meisterschaften und viele Informationen zu jedem. Hier sind einige Beispielabfragen:

  • Wie heißen die Spieler auf der aktuellen Liste der Golden State Warriors??
  • Wie heißen die Starter der Washington Wizards, ihre Höhe und ihr Alter??
  • Welcher aktive Trainer hat die meisten Meisterschaften??
  • Für welche Mannschaften und in welchen Jahren hat der Trainer seine Meisterschaften gewonnen?
  • Welcher Spieler hat die meisten MVP-Awards gewonnen??

Ich könnte Hunderte solcher Fragen stellen. Stellen Sie sich vor, Sie müssen eine API entwerfen, um alle diese Abfragen für das Front-End verfügbar zu machen, und Sie können die API problemlos mit neuen Abfragetypen erweitern, wenn Ihre Benutzer oder Ihr Produktmanager mit neuen aufregenden Dingen aufwarten.

Das ist nicht trivial. GraphQL wurde entwickelt, um genau dieses Problem anzugehen, und mit einem einzigen API-Endpunkt bietet es enorme Leistung, wie Sie bald sehen werden.

GraphQL vs. REST

Bevor wir in die Grundlagen von GraphQL eintauchen, vergleichen wir sie mit REST, dem derzeit beliebtesten Web-API.

REST folgt einem ressourcenorientierten Modell. Wenn unsere Ressourcen Spieler, Trainer und Teams sind, dann wird es wahrscheinlich Endpunkte geben, wie:

  • / Spieler 
  • / Spieler / 
  • / Reisebusse
  • / Reisebusse / 
  • / Mannschaften
  • / teams /

Oft geben die Endpunkte ohne ID nur eine Liste von IDs zurück, und die Endpunkte mit der ID geben die vollständigen Informationen zu einer Ressource zurück. Sie können Ihre API natürlich auch auf andere Weise entwerfen (z. B. kann der Endpunkt / players auch den Namen jedes Spielers oder alle Informationen zu jedem Spieler zurückgeben)..

Das Problem bei diesem Ansatz in einer dynamischen Umgebung ist, dass Sie entweder zu wenig abrufen (z. B. nur die IDs erhalten und weitere Informationen benötigen) oder zu viele Informationen abrufen (z. B. die vollständigen Informationen zu jedem Spieler, wenn Sie sich gerade befinden nur an dem Namen interessiert). 

Das sind harte Probleme. Wenn Sie beim Abrufen 100 IDs abrufen, müssen Sie 100 separate API-Aufrufe ausführen, um die Informationen zu jedem Player abzurufen. Beim Überholen verschwenden Sie viel Back-End-Zeit und Netzwerkbandbreite, indem Sie eine Menge nicht benötigter Daten vorbereiten und übertragen.

Es gibt Möglichkeiten, es mit REST anzusprechen. Sie können viele individuelle Endpunkte entwerfen, von denen jeder genau die Daten liefert, die Sie benötigen. Diese Lösung ist nicht skalierbar. Es ist schwer, die API konsistent zu halten. Es ist schwer, es weiterzuentwickeln. Es ist schwer zu dokumentieren und zu verwenden. Es ist schwer beizubehalten, wenn sich die einzelnen Endpunkte stark überschneiden.

Betrachten Sie diese zusätzlichen Endpunkte:

  • / Spieler / Namen
  • / players / names_and_championships
  • / team / starter

Ein anderer Ansatz besteht darin, eine kleine Anzahl generischer Endpunkte beizubehalten, aber viele Abfrageparameter bereitzustellen. Diese Lösung vermeidet das Problem mit vielen Endpunkten, ist jedoch dem REST-Modell zuwider, und es ist auch schwierig, sie konsequent weiterzuentwickeln und aufrechtzuerhalten.

Man könnte sagen, dass GraphQL diesen Ansatz an seine Grenzen gebracht hat. Es wird nicht in Form definierter Ressourcen gedacht, sondern in Form von Subgraphen der gesamten Domäne.

Das GraphQL-Typsystem

GraphQL modelliert die Domäne mit einem Typsystem, das aus Typen und Attributen besteht. Jedes Attribut hat einen Typ. Der Attributtyp kann einer der Basistypen sein, die GraphQL bereitstellt, wie ID, String und Boolean, oder ein benutzerdefinierter Typ. Die Knoten des Diagramms sind die benutzerdefinierten Typen und die Kanten sind die Attribute, die benutzerdefinierte Typen haben. 

Wenn zum Beispiel ein Typ "Spieler" ein Attribut "Team" mit dem Typ "Team" hat, bedeutet dies, dass zwischen jedem Spielerknoten und einem Teamknoten eine Kante besteht. Alle Typen sind in einem Schema definiert, das das GraphQL-Domänenobjektmodell beschreibt. 

Hier ist ein sehr vereinfachtes Schema für die NBA-Domäne. Der Spieler hat einen Namen, ein Team, mit dem er am meisten verbunden ist (ja, ich weiß, Spieler wechseln manchmal von einem Team zum anderen) und die Anzahl der Meisterschaften, die der Spieler gewonnen hat. 

Das Team hat einen Namen, eine Reihe von Spielern und die Anzahl der Meisterschaften, die das Team gewonnen hat.

Typ Spieler ID: ID Name: String! Team: Team! Meisterschaftsanzahl: Ganzzahl!  Typ Team ID: ID Name: Zeichenfolge! Spieler: [Player!]! Meisterschaftsanzahl: Ganzzahl!  

Es gibt auch vordefinierte Einstiegspunkte. Dies sind Abfrage, Mutation und Abonnement. Das Frontend kommuniziert mit dem Backend über die Einstiegspunkte und passt sie an seine Bedürfnisse an.

Hier ist eine Abfrage, die einfach alle Spieler zurückgibt:

Typ Abfrage allPlayers: [Player!]! 

Das Ausrufezeichen bedeutet, dass der Wert nicht null sein kann. Im Falle der allPlayers Abfrage kann eine leere Liste zurückgeben, aber nicht null. Es bedeutet auch, dass es keinen Null-Player in der Liste geben kann (weil er Player enthält!)..

Einrichten eines GraphQL-Servers

Hier ist ein vollwertiger GraphQL-Server, der auf node-express basiert. Es verfügt über einen hart codierten Datenspeicher im Arbeitsspeicher. Normalerweise befinden sich die Daten in einer Datenbank oder werden von einem anderen Dienst abgerufen. Die Daten werden hier definiert (entschuldigt sich im Voraus, wenn Ihre Lieblingsmannschaft oder Ihr Lieblingsspieler es nicht geschafft hat):

let data = "allPlayers": "1": "id": "1", "name": "Stephen Curry", "championshipCount": 2, "teamId": "3", "2": "id": "2", "name": "Michael Jordan", "championsCount": 6, "teamId": "1", "3": "id": "3", "name": "Scottie Pippen", "ChampionshipCount": 6, "TeamId": "1", "4": "id": "4", "Name": "Magic Johnson", "ChampionshipCount": 5, "TeamId ":" 2 "," 5 ": " id ":" 5 "," name ":" Kobe Bryant "," championshipCount ": 5," teamId ":" 2 "," 6 ": " id ":" 6 "," name ":" Kevin Durant "," championshipCount ": 1," teamId ":" 3 "," allTeams ": " 1 ": " id ":" 1 ", "Name": "Chicago Bulls", "ChampionshipCount": 6, "Spieler": [], "2": "id": "2", "Name": "Los Angeles Lakers", "ChampionshipCount": 16, "players": [], "3": "id": "3", "name": "Golden State Warriors", "championshipCount": 5, "players": [] 

Die Bibliotheken, die ich verwende, sind:

const express = erfordern ('express'); const graphqlHTTP = required ('express-graphql'); const app = express (); const buildSchema = required ('graphql'); const _ = required ('lodash / core');

Dies ist der Code zum Erstellen des Schemas. Beachten Sie, dass ich einige Variablen hinzugefügt habe allPlayers Root-Abfrage.

schema = buildSchema ('type Player id: ID name: String! championshipCount: Int! team: Team! type Team id: ID name: String! championshipCount: Int! players: [Player!]! (Offset: Int = 0, Limit: Int = -1): [Player!]! ' 

Hier kommt der Schlüssel dazu: die Abfragen verbinden und die Daten tatsächlich bereitstellen. Das rootValue Objekt kann mehrere Wurzeln enthalten. 

Hier gibt es nur die allPlayers. Es extrahiert den Versatz und das Limit aus den Argumenten, schneidet die Daten aller Spieler und legt dann das Team für jeden Spieler anhand der Team-ID fest. Dies macht jeden Spieler zu einem verschachtelten Objekt.

rootValue = allPlayers: (args) => offset = args ['offset'] limit = args ['limit'] r = _.values ​​(data ["allPlayers"]). slice (offset) if (limit> - 1) r = r.slice (0, Math.min (limit, r.length)) _.forEach (r, (x) => data.allPlayers [x.id] .team = data.allTeams [ x.teamId]) return r, 

Schließlich ist hier der graphql Endpunkt, Übergeben des Schemas und des Stammwertobjekts:

app.use ('/ graphql', graphqlHTTP (schema: schema, rootValue: rootValue, graphiql: true)); App.listen (3000); module.exports = App;

Rahmen graphiql zu wahr ermöglicht es uns, den Server mit einer fantastischen In-Browser-GraphQL-IDE zu testen. Ich habe es sehr empfohlen, um mit verschiedenen Abfragen zu experimentieren.

Ad-hoc-Abfragen mit GraphQL

Alles ist eingestellt. Navigieren wir zu http: // localhost: 3000 / graphql und haben etwas Spaß.

Wir können einfach mit einer Liste der Spielernamen beginnen:

Abfrage justNames allPlayers Name Ausgabe: "Daten": "AllPlayers": ["Name": "Stephen Curry", "Name": "Michael Jordan", "Name": "Scottie Pippen ", " Name ":" Magic Johnson ", " Name ":" Kobe Bryant ", " Name ":" Kevin Durant "] 

In Ordung. Wir haben hier einige Superstars. Kein Zweifel. Lasst uns etwas Begabteres gehen: Ausgehend von Offset 4 bekommen Sie 2 Spieler. Geben Sie für jeden Spieler seinen Namen und die Anzahl der gewonnenen Meisterschaften sowie den Namen des Teams und die Anzahl der Meisterschaften an, die das Team gewonnen hat.

query twoPlayers allPlayers (Offset: 4, Limit: 2) Name MeisterschaftCount Team Name MeisterschaftCount Ausgabe: "Daten": "AllPlayers": ["Name": "Kobe Bryant", "ChampionshipCount": 5, "Team": "Name": "Los Angeles Lakers", "ChampionshipCount": 16, "Name": "Kevin Durant", "ChampionshipCount": 1, "Team": "Name": "Golden State Warriors", "ChampionshipCount": 5] 

So gewann Kobe Bryant fünf Meisterschaften mit den Lakers, die insgesamt 16 Meisterschaften gewannen. Kevin Durant gewann mit den Warriors nur eine Meisterschaft, die insgesamt fünf Meisterschaften gewann.

GraphQL-Mutationen

Magic Johnson war sicher ein Magier auf dem Platz. Aber ohne seinen Kumpel Kareem Abdul-Jabbar hätte er es nicht geschafft. Fügen wir Kareem zu unserer Datenbank hinzu. Wir können GraphQL-Mutationen definieren, um Operationen wie das Hinzufügen, Aktualisieren und Entfernen von Daten aus unserem Graphen auszuführen.

Zuerst fügen wir dem Schema einen Mutationstyp hinzu. Es sieht ein bisschen wie eine Funktionssignatur aus:

Typ Mutation createPlayer (Name: String, Meisterschaftsanzahl: Int, Team-ID: String): Spieler

Dann müssen wir es implementieren und zum root-Wert hinzufügen. Die Implementierung übernimmt einfach die von der Abfrage bereitgestellten Parameter und fügt ein neues Objekt hinzu Daten ['allPlayers']. Es stellt auch sicher, dass das Team richtig eingestellt ist. Schließlich wird der neue Spieler zurückgegeben.

 createPlayer: (args) => id = (_.values ​​(data ['allPlayers']). length + 1) .toString () args ['id'] = id args ['team'] = data ['allTeams '] [args [' teamId ']] data [' allPlayers '] [id] = args gibt Daten zurück [' allPlayers '] [id],

Um Kareem tatsächlich hinzuzufügen, können wir die Mutation aufrufen und den zurückgegebenen Spieler abfragen:

Mutation addKareem createPlayer (Name: "Kareem Abdul-Jabbar", MeisterschaftCount: 6, TeamId: "2") Name MeisterschaftCount Team Name Ausgabe: "Daten": "CreatePlayer": "Name": "Kareem Abdul-Jabbar", "ChampionshipCount": 6, "Team": "Name": "Los Angeles Lakers" 

Hier ist ein dunkles kleines Geheimnis über Mutationen… sie sind eigentlich genau wie Abfragen. Sie können Ihre Daten in einer Abfrage ändern, und Sie können nur Daten aus einer Mutation zurückgeben. GraphQL wird keinen Einblick in Ihren Code erhalten. Sowohl Abfragen als auch Mutationen können Argumente annehmen und Daten zurückgeben. Es ist eher wie syntaktischer Zucker, um Ihr Schema für den Menschen lesbarer zu machen.

Fortgeschrittene Themen

Abonnements

Abonnements sind ein weiteres Killer-Feature von GraphQL. Mit Abonnements kann der Client Ereignisse abonnieren, die ausgelöst werden, wenn sich der Serverstatus ändert. Abonnements wurden zu einem späteren Zeitpunkt eingeführt und auf unterschiedliche Weise von verschiedenen Frameworks implementiert.

Validierung

GraphQL überprüft jede Abfrage oder Mutation anhand des Schemas. Dies ist ein großer Gewinn, wenn die Eingabedaten eine komplexe Form haben. Sie müssen keinen ärgerlichen und spröden Validierungscode schreiben. GraphQL kümmert sich für Sie darum. 

Schema Introspektion

Sie können das aktuelle Schema selbst überprüfen und abfragen. Das gibt Ihnen Metakräfte, um das Schema dynamisch zu entdecken. Hier ist eine Abfrage, die alle Typnamen und ihre Beschreibung zurückgibt:

Abfrage q __schema Typen Namensbeschreibung

Fazit

GraphQL ist eine aufregende neue API-Technologie, die viele Vorteile gegenüber REST-APIs bietet. Dahinter verbirgt sich eine lebendige Community, ganz zu schweigen von Facebook. Ich gehe davon aus, dass es sich in kürzester Zeit zu einer festen Größe entwickeln wird. Versuche es. Du wirst es mögen.