Was man über GraphQL wissen sollte

the next big thing Golo Roden  –  22 Kommentare

GraphQL ist eine Sprache zum Abfragen und Ändern von Graphen. Ursprünglich von Facebook für interne Zwecke entwickelt, ist GraphQL seit 2015 als Open Source verfügbar. Was sollte man über GraphQL wissen?

GraphQL, was für "Graph Query Language" steht, ist eine ursprünglich im Jahr 2012 von Facebook für die interne Verwendung entwickelte Sprache, mit der sich in Graphen vorliegende Daten effizient abfragen und gezielt ändern lassen. 2015 wurde GraphQL dann veröffentlicht und als Open Source unter der MIT-Lizenz freigegeben. Inzwischen liegt die Weiterentwicklung in den Händen der eigens dafür gegründeten GraphQL Foundation, die zur Linux Foundation gehört.

Die Sprache ähnelt prinzipiell SQL, zielt aber nicht auf den Einsatz in Datenbanken ab, sondern ist eine Abfragesprache für APIs im Web und in der Cloud. Dabei ist GraphQL unabhängig von einer konkreten Programmiersprache, einer bestimmten Plattform oder speziellen Protokollen und daher universell und technologieübergreifend einsetzbar.

Bei GraphQL gibt der Client vor, welche Daten er vom Server abrufen möchte, wodurch die Sprache wesentlich leichtgewichtiger als REST ist, da keine unnötigen Daten übertragen werden müssen. Anders als REST ist GraphQL zudem in weitaus höherem Maße standardisiert und statisch typisiert, was die Integration und Interoperabilität erleichtert.

Anders als es der Name nahelegt, handelt es sich bei GraphQL aber nicht um eine reine Abfragesprache. Zwar wird das Lesen von Daten über sogenannte "Queries" unterstützt, Daten können aber durch "Mutations" auch geändert werden. Darüber hinaus kann sich ein Client auf Änderungen an den Daten in Echtzeit informieren lassen, wofür es das Konzept der "Subscriptions" gibt.

Diese drei Vorgänge werden gemeinsam mit den grundlegenden Datentypen und deren Beziehungen zueinander in einem Schema definiert, das in der GraphQL Schema Definition Language (SDL) geschrieben wird. Diese ist seit Februar 2018 ebenfalls Bestandteil der Spezifikation.

Was ist GraphQL?

Queries, Mutations & Co.

Eine Query ähnelt einem JSON-Dokument, wobei nur die Schlüssel enthalten sind: Die Werte sind ja gerade das, was mit einer Query abgefragt werden soll. Der Client bestimmt über die in der Query angegebene Struktur, welche Datentypen und Beziehungen er als relevant erachtet. Die Query und auch das Ergebnis sind dabei statisch typisiert.

Anders als bei REST benötigt man für jegliche Queries nur einen einzigen Endpunkt, der typischerweise /graphql lautet. Technisch kann eine Query mit Hilfe eines GET- oder eines POST-Requests übertragen werden: Im ersten Fall werden die eigentlichen Daten der Query im Querystring übergeben und im zweiten Fall innerhalb des Request-Bodys.

Mutations, die dem Ändern von Daten dienen, sind ebenfalls typsicher und innerhalb des Schemas definiert. Sie stellen Funktionen dar und bilden daher eine Art RPC-Ansatz. Wie nicht anders zu erwarten lassen sich Parameter an Mutations übergeben und Variablen verwenden. Versendet werden Mutations stets über POST, wobei der gleiche Endpunkt verwendet wird wie für Queries.

Subscriptions schließlich ermöglichen das Abonnieren von Änderungen an Daten, so dass sich Clients per Push-Benachrichtigung informieren lassen können. Auch sie arbeiten typsicher, basieren in der Regel aber auf Websockets statt auf GET- oder POST-Requests.

Queries, Mutations & Co. in GraphQL

Resolver

Die Aufgabe eines GraphQL-Servers besteht darin, die vom Client gesendeten Queries, Mutations und Subscriptions entgegenzunehmen, zu parsen, zu verarbeiten und zu beantworten. Dabei gilt es, technische und fachliche Aspekte zu unterscheiden.

Das Entgegennehmen, Parsen und Beantworten stellt dabei die technischen Aspekte dar. Für sie gibt es fertige Module für zahlreiche Programmiersprachen und Plattformen, beispielsweise "graphqljs" für JavaScript, TypeScript und Node.js.

Der für die fachliche Sicht relevante Aspekt ist das Verarbeiten der eingehenden Anfragen. Hier ist besonders die Frage relevant, wie die eigentlichen Datenabfragen und -änderungen ausgeführt werden. Zu dem Zweck obliegt es Entwicklerinnen und Entwicklern, Funktionen zu schreiben und auf dem Server zu hinterlegen, sogenannte "Resolver". Deren Aufgabe ist, für jedes einzelne angefragte Feld die passenden Daten zu ermitteln, sei es aus einer Datenbank, einer API, einem Objekt, oder einer sonstigen Datenquelle.

Um eine möglichst gute Performance zu erzielen, wird die Ausführung von Resolvern nach Möglichkeit parallelisiert, wobei das – je nach Beziehung der abzufragenden Daten – in einigen Situationen nicht möglich ist. Außerdem erhält jeder Resolver das Ergebnis des übergeordneten Resolver übergeben. Auf dem Weg wird verhindert, dass für jedes einzelne Feld eine eigene Datenbankabfrage ausgeführt werden muss.

Resolver in GraphQL

GraphQL versus REST

Vergleicht man nun GraphQL und REST, unterscheiden sie sich auf den ersten Blick primär darin, dass GraphQL mit einem einzigen Endpunkt auskommt, wohingegen REST für jeden Datentyp und jede Identität einen eigenen Endpunkt bietet. Während in REST ein Endpunkt und eine Identität also aneinander gekoppelt sind, entkoppelt GraphQL diese beiden ganz bewusst. Das spricht tendenziell eher für GraphQL als für REST.

Es gibt allerdings noch zahlreiche weitere Vorteile, die für GraphQL sprechen. So geben REST-APIs häufig zu viele oder zu wenige Daten zurück, weshalb entweder unnötige Daten übertragen werden müssen oder mehrere Requests erforderlich sind. Diese Probleme sind als "Overfetching" beziehungsweise "Underfetching" bekannt. Außerdem vermeidet GraphQL das "N+1"-Problem, bei dem beispielsweise für die Details zu den Einträgen einer Liste jeweils eigene Aufrufe erforderlich sind.

Zu guter Letzt sprechen auch die Typsicherheit und die einfache Versionierbarkeit für GraphQL. Typsicherheit lässt sich in REST nur bedingt mit Hilfe von OpenAPI und JSON-Schemata sicherstellen, Versionierung ist relativ umständlich. In GraphQL hingegen lassen sich einem Datentyp einfach weitere Felder hinzufügen, die – sofern es sich nicht um inkompatible Änderungen handelt – für die bestehenden Clients zunächst unsichtbar bleiben.

Trotzdem gibt es nicht nur Argument für GraphQL, einiges spricht auch gegen den Einsatz dieser Sprache. Dazu zählt die fehlende Möglichkeit, auf Clientseite mit Caching zu arbeiten, da nur ein einziger Endpunkt verwendet wird. An der Stelle folgt GraphQL bewusst nicht den Grundsätzen von HTTP. Auch die clientseitige Auswahl der Daten, die eigentlich zu den Stärken von GraphQL zählt, kann problematisch sein, wenn Abfragen nämlich zu umfangreich werden und den Server potentiell lahmlegen.

Das statische Typsystem, das ebenfalls zu den Stärken von GraphQL zählt, kann zu einem Nachteil werden, wenn sich keine statischen Typen definieren und angeben lassen. Soll eine API beispielsweise JSON-Objekte unterschiedlicher Art zurückgeben, lässt sich das in GraphQL nur schlecht bis gar nicht ausdrücken. Hierfür muss man gegebenenfalls auf als Strings serialisiertes JSON zurückgreifen.

GraphQL versus REST

Best Practices

Der größte Unterschied zwischen GraphQL und REST besteht aber darin, dass GraphQL auf Grund der viel besseren Möglichkeiten, Semantik auszudrücken, weitaus fachlicher angelegt ist als REST. Trotzdem bedeutet das nicht, dass man die Geschäftslogik einer Anwendung in den Resolvern unterbringen sollte.

Die eigentliche Fachlichkeit sollte, unabhängig von GraphQL, stets in einer eigenen Schicht beziehungsweise in eigenen Komponenten enthalten sein, auf die von GraphQL oder einer anderen API aus lediglich zugegriffen wird. Auch Querschnittsbelange wie Authentifizierung, Autorisierung, CORS & Co. sollten nicht in GraphQL selbst behandelt werden, sondern in eigenständigen Modulen.

Am wichtigsten dabei ist jedoch, sich stets vor Augen zu führen, dass GraphQL die fachliche Domäne repräsentieren sollte und keine technischen Konstrukte wie Datenbankschemas. Sobald beispielsweise Begriffe wie "Create", "Update" und "Delete" in Mutations auftauchen, ist das häufig ein klares Indiz dafür, dass die API zu technisch geplant und angelegt wurde.

Die Sprache der API sollte der Sprache der Anwenderinnen und Anwender entsprechen, was die Dokumentation von GraphQL als "Shared Language" beziehungsweise "Everyday Language" bezeichnet. Aus dem Grund passt GraphQL auch hervorragend zu Domain-Driven Design (DDD), das ebenfalls in diese Richtung geht.

Best-Practices in GraphQL

Fazit

GraphQL ist eine interessante Sprache, mit der sich APIs modellieren lassen, die nicht auf REST basieren. Die größte Stärke von GraphQL ist dabei die Möglichkeit, APIs weitaus besser fachlich beschreiben zu können, was in Verbindung mit der von Haus aus enthaltenen Typsicherheit zu besseren Abfragen und Änderungsaufträgen führt.

Da GraphQL zudem prinzipbedingt das Over- und Underfetching von REST-APIs vermeidet, eignet es sich hervorragend für umfangreiche und komplexe APIs, die ein hohes Maß an Fachlichkeit und Geschäftsprozessen abbilden müssen. Auch die Möglichkeit, sich mit GraphQL direkt über Updates an den Daten informieren zu lassen, ist ein wesentlicher Pluspunkt gegenüber REST.