Einführung in GraphQL
Heute möchte ich darüber berichten, wie man ein serverless Backend mit der Hasura GraphQL API erstellt. Als erstes werden wir lernen, was GraphQL ist und weshalb es immer mehr an Beliebtheit gewinnt. Danach werden wir unsere eigene GraphQL API mit Hilfe von Hasura erstellen, Tabellen hinzufügen und diese mit Daten füllen. Am Schluss lernen wir, wie wir die Tabellen mit Hilfe von Mutationen manipulieren können. Es sind keine GraphQL Vorkenntnisse nötig, um diesem Blogpost folgen zu können.
Inhaltsverzeichnis
- Über GraphQL
- Über Hasura
- Projekt erstellen
- Tabellen erstellen
- Daten einfügen
- Abfragen
- Beziehungen
- Objekt-Beziehung
- Array-Beziehung
- Viele-zu-viele Beziehung
- Mutationen
- Hasura Community
Über GraphQL
GraphQL ist eine typisierte Abfragesprache (query language) für APIs. Immer mehr Tech-Firmen wechseln von eher traditionellen REST-Architekturen zu Lösungen mit GraphQL, so zum Beispiel Twitter, Facebook und Github. Folgende Vorteile hat GraphQL im Vergleich zu REST:
- Man bekommt genau die Daten, die man anfordert (nicht mehr und nicht weniger)
- Verlässliche Resultate und hilfreiche Fehlermeldungen
- Effiziente und leichtgewichtige Applikationen, weil die Daten direkt abgerufen werden (statt über einen Server)
- Mehrere Ressourcen gleichzeitig abfragen ohne auf verschiedene URLs zuzugreifen
- Einen einzigen Endpoint für alle Ressourcen und typisierte Felder für korrekten Datenbankzugriff
- Kann einfach einem existierenden Projekt hinzugefügt werden
Hier eine Abfrage als Beispiel:
Und das entsprechende Resultat:
Wie man unschwer erkennen kann, ist die Abfrage sehr intuitiv und das Resultat ist vorhersehbar. Man bekommt genau das, was man abfragt — nicht mehr und nicht weniger.
Da Web-Applikationen tendenziell immer grösser und komplexer werden, ist die Anforderung für effiziente und einfach wartbare Lösungen gestiegen. Deshalb könnte jetzt ein guter Moment sein, sich GraphQL etwas näher anzuschauen. Aber wie wird es benutzt? Muss ich meinen eigenen GraphQL-Server dafür bauen? Und wie kann ich den Server mit meinem Frontend verbinden? All dies schauen wir uns nun an.
Über Hasura
Hasura ist eine open source GraphQL-Maschine, die meistens in einem Docker-Container läuft. Hasura verbindet zu einer Postgres-Datenbank, die mit dem Projekt zusammen erstellt wird. Man kann Hasura auch einem existierenden Projekt hinzufügen. Dies kann dann nützlich sein, wenn man ein grösseres Projekt zu GraphQL migrieren möchte, da es einem erlaubt, die Migration schrittweise zu machen.
Es gibt verschiedene Optionen, wo eine GraphQL API deployed werden kann: Heroku, Docker, Digital Ocean, Azure, AWS und Google Cloud sind einige Beispiele. Grundsätzlich überall, wo Docker auch läuft.
Projekt erstellen
In diesem Blogpost wollen wir zusammen eine Harry Potter API erstellen 🤓
Wähle einen (einzigartigen) Namen für deine API auf dem Heroku Dashboard und klicke den “Deploy”-Button.
Das war sehr einfach, oder? :-) Jetzt haben wir bereits unser erstes Hasura-Projekt erstellt. Yay! Wenn wir jetzt nach unten scrollen, sehen wir dies:
Wenn wir jetzt auf “View” klicken, öffnet sich die Hasura-Konsole im GraphiQL-Tool. Die Hasura-Konsole macht uns das Leben mit GraphQL bedeutend einfacher, da sie es uns erlaubt, innerhalb der Konsole Tabellen zu erstellen und Abfragen zu testen. Lasst uns die Konsole genauer anschauen:
- Das ist unser (einziger) Endpoint, den wir brauchen, um mit der API zu interagieren. Wenn wir später Daten abfragen möchten (zum Beispiel von einem Frontend aus), wird dies immer über diese URL passieren. Und ja, du hast richtig erkannt, dass Anfragen bei GraphQL immer POST-Abfragen sind.
- Hier kann man Request Headers hinzufügen. Wenn man später Authentifizierung hat (zum Beispiel mit JWT-Tokens), werden die Daten hier mitgegeben.
- Hier kann man Abfragen und Mutationen testen.
- Hier werden die Resultate angezeigt.
- Hier hin gehen wir als nächstes, um unsere erste Tabelle zu erstellen.
Tabellen erstellen
Zuerst wollen wir eine Tabelle erstellen, um Movies zu speichern. Let’s do it!
- Name der Tabelle.
- Jede Tabelle muss eine Spalte ID haben. Dies kann einen automatisch hochzählenden Integer sein oder eine UUID. Wir benutzen in unserem Beispiel UUID. Wir wollen aber nicht jedes Mal selber eine UUID mitgeben beim Erstellen eines neuen Movies. Zum Glück kommt Hasura mit einer Methode, mit dieser man UUIDs automatisch generieren kann.
- Mehr Felder hinzufügen.
- Wir müssen für jede Tabelle einen Primary Key definieren. In unserem Fall ist dies die ID.
Deine Aufgabe: Erstelle die folgenden zwei Tabellen:
characters (id: UUID, name: Text, hair_color: Text, house: Text, wizard: Boolean, birth_year: Integer, patronus: Text)
actors (id: UUID, name: Text, birth_year: Integer, awards: Integer)
Daten einfügen
Lasst uns ein paar Daten einfügen:
Füge mindestens einen weiteren Character hinzu, sowie zwei Movies und zwei Actors.
Abfragen
Jetzt, wo wir einige Daten in unserer Datenbank haben, können wir unsere erste Abfrage machen in der Hasura-Konsole.
Diese Abfrage wird uns die (mindestens) zwei Characters zurück geben, die wir in unserer Characters-Tabelle haben.
Es gibt diverse Einschränkungen, die man einer Abfrage hinzufügen kann. Zum Beispiel kann man sagen, dass man nur eine bestimmte Anzahl von Objekten anzeigen möchte oder dass man nur diejenigen Objekte haben möchte, für die eine bestimmte Kondition zutrifft. Dies alles ist von Hasura sehr gut dokumentiert: https://docs.hasura.io/1.0/graphql/manual/queries/simple-object-queries.html. Du kannst in die Dokumentation rein schauen und deine Abfragen so ändern, dass sie verschiedene Resultate zurück geben. Um dies zu tun, wäre es wahrscheinlich sinnvoll, weitere Daten hinzuzufügen.
Beziehungen
Im Moment haben wir drei Tabellen, die komplett unabhängig voneinander sind. Wir können Movies, Characters und Actors abrufen, aber wir können (noch) nicht Movies mit ihren entsprechenden Characters und deren Actors abrufen. Um dies zu tun, brauchen wir Beziehungen (relationships).
Es gibt in GraphQL zwei verschiedene Arten von Beziehungen: die Objekt-Beziehung und die Array-Beziehung. Die Objekt-Beziehung ist eine eins-zu-eins Beziehung. Zum Beispiel hat ein Character einen Actor. Die Array-Beziehung ist eine eins-zu-viele Beziehung. Zum Beispiel kann ein Movie ein Array von Szenen haben.
Objekt-Beziehung
Als erstes modellieren wir die Beziehung zwischen Characters und Actors. Der erste Schritt dazu ist, der Character-Tabelle eine actor_id
hinzuzufügen:
Nachdem wir die Spalte hinzugefügt haben, müssen wir sie bearbeiten und angeben, dass die actor_id
einen Foreign Key ist, der auf die Actors-Tabelle zeigt.
Wenn wir jetzt auf der Characters-Tabelle auf “Relationships” klicken, wird uns eine Beziehung vorgeschlagen. Genau richtig — Hasura erkennt Foreign Keys automatisch und macht Vorschläge, welche Beziehungen man einer Tabelle hinzufügen kann. Wir fügen diese Beziehung hinzu und nennen sie “actor”.
Als nächstes möchten wir die Characters mit ihren entsprechenden Actors in unserer Datenbank verbinden. Wenn wir uns die Characters-Tabelle anschauen, sehen wir, dass die actor_id
für die bisher erstellten Characters NULL ist. Jetzt können wir die Daten editieren und die entsprechenden IDs der Actors einfügen.
Jetzt sind die Characters und Actors miteinander verbunden. Sehr schön! Wenn wir jetzt eine Abfrage machen, können wir die Characters mit ihren entsprechenden Actors zusammen abfragen:
Array-Beziehung
Die Array-Beziehung ist eine eins-zu-viele Beziehung. Das heisst, dass ein Objekt einer Tabelle mehrere Objekte einer anderen Tabelle haben kann. In unserem Beispiel kann ein Movie mehrere Szenen haben und jede Szene gehört zu einem Movie. Eine Szene hat eine id
, einen name
, eine location
und eine movie_id
. Lass uns diese Tabelle namens scenes
in unserer Datenbank erstellen:
Super! Jetzt können wir — wie vorher — die Tabelle bearbeiten und die movie_id
zu einem Foreign Key machen:
Viele-zu-viele Beziehung
Wie bereits erklärt ist die Array-Beziehung eine eins-zu-viele Beziehung. Jedoch haben wir einen speziellen Fall bei unserer Movies-Tabelle und unserer Characters-Tabelle. Hier haben wir nämlich eine viele-zu-viele Beziehung. Ein Movie kann mehrere Characters haben und ein Character kann in mehreren Movies vorkommen. Für dieses Szenario brauchen wir eine Join-Tabelle, die wir movie_characters
nennen. Diese Tabelle ist dafür da, die Beziehung zwischen einem Movie und einem Character zu speichern. Lasst uns diese Tabelle erstellen. Sie hat eine id
, eine movie_id
und eine character_id
.
Von der Perspektive der Movie Characters-Tabelle besteht eine Objekt-Beziehung zur Movies-Tabelle und zur Characters-Tabelle. Dies ist, weil in jedem Movie Character ein einzelner Movie und ein einzelner Character gespeichert ist.
Wie oben, müssen wir sowohl die movie_id
, wie auch die character_id
in der movie_characters
Tabelle bearbeiten, diese zu Foreign Keys machen und die korrekte Tabellen-Referenz angeben. Für die movie_id
ist dies die Tabelle Movies und für die character_id
die Tabelle Characters. In beiden Fällen ist die Referenz-Spalte die ID.
Wenn wir jetzt auf “Relationships” klicken, sehen wir zwei Vorschläge für Objekt-Beziehungen. Wir fügen diese beide hinzu und nennen sie “movie” und “character”. Sobald hinzugefügt, sollte das Ganze so aussehen:
Von der Perspektive der Characters-Tabelle und der Movies-Tabelle sind es jeweils Array-Beziehungen, da ein Movie oder ein Character mehrere Movie Characters haben kann. Wenn wir unter “Data” auf die Tabelle “Movies” klicken und dann “Relationships”, schlägt es uns eine Array-Beziehung zu Movie Characters vor. Wir fügen diese hinzu und nennen sie “movie_characters”. Genau dasselbe machen wir für die Characters-Tabelle.
Natürlich müssen wir — wie oben — die Beziehungen innerhalb unserer Daten noch hinzufügen. Erstelle neue Objekte in der Movie Characters-Tabelle und füge je einen Movie und einen Character hinzu, indem du deren ID benutzt.
Yayy, nun sind unsere Tabellen korrekt modelliert inklusive ihren Beziehungen. Lasst uns dies mit einer Abfrage testen.
Sehr schön! Mit nur einer Abfrage können wir nun alle von uns gewünschten Ressourcen auf einmal abfragen. Dies im Vergleich zu REST, wo wir normalerweise das ganze Objekt empfangen und dann diejenigen Felder auslesen müssen, die wir brauchen. Indem wir nur das zurück bekommen, wonach wir fragen, ist unsere API effizienter, leichtgewichtiger und einfacher im Umgang.
Mutationen
Bisher haben wir gelernt, wie wir Daten von unserer API abfragen können. Aber wie können wir neue Daten hinzufügen? In unserem Fall möchten wir zum Beispiel einen neuen Movie oder einen neuen Character hinzufügen. Für dies brauchen wir Mutationen. Mutationen sind sehr einfach mit GraphQL. Genau wie Abfragen können wir auch Mutationen in der Hasura-Konsole testen:
Zur Erklärung, was hier alles passiert: innerhalb einer Mutation können wir verschiedene Methoden aufrufen auf unseren Ressourcen, wie zum Beispiel insert
, update
oder delete
. Wir möchten eine Mutation erstellen, die es uns erlaubt, einen neuen Movie hinzuzufügen. Wir müssen die Movies als objects
-Array übergeben, da es möglich ist, mehrere Movies in einer einzelnen Mutation hinzuzufügen. Jede Mutation muss etwas zurückgeben und wir geben in unserem Fall einfach die ID zurück des neu erstellten Movies.
Auch hier ist die Dokumentation auf der Hasura-Webseite ausgezeichnet: https://docs.hasura.io/1.0/graphql/manual/mutations/index.html. Gehe durch die Beispiele und versuche, eine andere Mutation zu schreiben. Zum Beispiel eine, die es erlaubt, einen Movie zu bearbeiten oder zu löschen.
Das ist es für heute. Ich hoffe, es hat Spass gemacht, die ersten Schritte mit GraphQL zu machen und ich hoffe, dass es bald viele Harry Potter APIs da draussen gibt mit vielen Tabellen und Daten. Wenn etwas unklar ist, bin ich per Email (marion.schleifer@gmail.com) oder Twitter (https://twitter.com/rubydwarf) erreichbar.
Wenn du Lust hast, gleich weiter zu lernen, dann lese hier meinen Blogpost zum Verbinden eines GraphQL-Backends mit einem VueJS-Frontend: https://medium.com/@marion.schleifer/how-to-connect-your-graphql-api-to-your-vuejs-frontend-61d8e8e455db.
Hasura Community
Hasura wird zunehmend mehr benutzt von Entwicklern aus aller Welt. Die Community ist sehr offen und freundlich und wird sich freuen, dich dabei zu haben.
Wenn du deine eigenen Hasura-Projekte starten möchtest, wenn du Fragen hast oder Vorschläge für neue Funktionen, kannst du dem Discord-Channel beitreten: https://discordapp.com/invite/hasura. Die Community ist ziemlich gross und Fragen werden üblicherweise sehr schnell beantwortet.
🐦 Twitter: https://twitter.com/hasurahq
⭐️ Github: https://github.com/hasura/graphql-engine