GraphQL als API-Gateway – Teil 1: Grundlagen, Theorie, Design
Seite 2: Was macht ein API-Gateway?
Das API-Gateway ist ein API-Management-Tool, das zwischen einem Client und mehreren Backend-Services moderiert. Er erhält als Reverse Proxy alle API-Anfragen des Clients, delegiert sie an die entsprechenden Backend-Services weiter und gibt am Ende die erwünschte Antwort zurück.
Auf diese Weise führt ein API-Gateway mehrere APIs unterschiedlicher Services zusammen. Davon profitieren insbesondere Microservicearchitekturen, wie das Beispiel eines Onlineshops verdeutlicht, der in unterschiedliche (Micro-)Services für Produkte, Warenkörbe und Rechnungen unterteilt ist. Rechnungen referenzieren Warenkörbe, die wiederum Produkte referenzieren. Soll ein neues Team eine Applikation entwickeln, die Kunden Rechnungen mitsamt den Produktdetails im gekauften Warenkorb visualisiert, steht es vor einem Problem: Da es Informationen von drei unterschiedlichen Backend-Services benötigt, müsste die Applikation ohne ein API-Gateway die Adressen aller drei Services kennen. Sollte sich zudem eine der drei APIs ändern, müsste das Team auch jedes Mal direkt die Applikation anpassen.
Liegt zwischen der neuen Applikation und den drei Backend-Services jedoch ein API-Gateway, lassen sich die direkten Zugriffe auf dieses Gateway abstrahieren. Es genügt, die Adresse zum Gateway zu kennen. Darüber hinaus erfordert eine Änderung in den APIs der Backend-Services nicht mehr zwangsläufig eine Anpassung in der Applikation. Auch kann das API-Gateway ein einheitliches Schema einführen – etwa, wenn es sich bei den APIs der Backend-Services um unterschiedliche Technologien oder Paradigmen handelt.
Wie funktioniert das API-Gateway-Pattern mit GraphQL?
Ein API-Gateway empfängt Anfragen eines Clients und delegiert diese an verschiedene Backend-Services. Ein GraphQL-Server empfängt Anfragen eines Clients und delegiert diese an verschiedene Services im Backend.
Nun kommt die Besonderheit: Da GraphQL eine plattformunabhängige Spezifikation einer Query-Sprache ist, sind die Resolver, die Client-Anfragen für die Geschäftslogik übersetzen, beim Auflösen der Querys nicht auf einen bestimmten Service festgelegt. Der betreffende Service muss sich nicht einmal in derselben Applikation befinden. Resolver können Anfragen daher auch an andere (GraphQL-)APIs delegieren. Ein übergreifendes GraphQL Schema kann somit als API-Gateway über die Resolver den Kontakt zu anderen Schnittstellen moderieren.
Wollen Entwicklerinnen und Entwickler mehrere GraphQL-APIs mit einem GraphQL-Server als API-Gateway orchestrieren, kann der Gedanke, ein übergreifendes Schema für all diese Services zu entwickeln, unter Umständen abschreckend sein. Händisches Duplizieren der Schemas in das API-Gateway birgt die Gefahr, schnell out of sync zu gehen, also veraltet zu sein. Jede Anpassung am Schema eines der darunterliegenden Services führt direkt auch zu Anpassungsbedarf im darüberliegenden Gateway. Die Änderungen am übergreifenden Schema potenziell über mehrere Teams hinweg zu koordinieren, birgt viele Risiken und Konflikte.
Abhilfe schafft hier die GraphQL-Community mit vielen Hilfspaketen und Erweiterungen, beispielsweise mit dem npm-Package graphql-tools in JavaScript. Diese Kollektion hilfreicher Werkzeuge für GraphQL-Server führte 2018 das Schema-Stitching ein. Damit lassen sich auf programmatische Art mehrere GraphQL-Schemas zu einem mergen beziehungsweise zusammennähen. Das funktioniert vor allem über die folgenden Funktionen:
- Erstellung eines Executors
Ein Executor-Objekt sorgt ĂĽber eine HTTP-Client-Implementierung dafĂĽr, dass Requests gegen das Schema eines anderen GraphQL-Server ausgefĂĽhrt werden.
introspectSchema
Die Introspection ermöglicht es, durch Querys nicht nur Daten, sondern auch Typ-Informationen über das Schema der angefragten API zu erhalten. In dieser Funktion nutzt GraphQL die Introspection, um mit dem Executor eine Schema-Instanz aus den Typinformationen einer GraphQL-API zu erzeugen.
wrapSchema
Diese Funktion nutzt die Möglichkeit der beiden vorher genannten, um aus einem durch die Introspection erforschten, entfernten GraphQL-API ein ausführbares Schema zu erzeugen, dessen Resolver Anfragen automatisch über den Executor an das entfernte GraphQL-API weiterleitet.
mergeSchema
Diese Funktion nutzt die Möglichkeit in einem GraphQL-Schema, über extend type eine bestehende Typdefinition mit zusätzlichen Feldern zu erweitern. Die Eingabe einer Liste ausführbarer GraphQL-Schemas erzeugt durch diese Funktion ein einzelnes Schema, das – soweit konfliktfrei möglich – alle Felder der eingegebenen Schemas enthält.
Use Case eines kleinen Onlineshops als Versuchskaninchen
Primäres Ziel dieses Artikels ist es nicht, die Grundlagen von GraphQL erschöpfend zu erörtern, sondern das Einsatzgebiet eines API-Gateway mit GraphQL näher zu betrachten. Wer tiefer in die Grundlagen der Schema Definition Language eintauchen möchte, dem seien der Beitrag Java-Anwendungen mit GraphQL von Nils Hartmann oder das Buch "GraphQL – Eine Einführung in APIs mit GraphQL" des Autors empfohlen.
Der praktische Teil des Buches dreht sich um die Entwicklung zweier GraphQL-Server, die öffentlich auf GitHub zur Verfügung stehen. Die beiden GraphQL-Server dienen hier als Grundlage für die beispielhafte Implementierung des API-Gateway. Die Server sind jeweils in JavaScript und Java geschrieben. Sie implementieren je einen Use Case eines Onlineshops – dem sogenannten Graphshop.
Die JavaScript-Version erstellt eine GraphQL-API, die Produkte verwaltet (im Schema Product genannt, mit den Feldern id und name). Diese Produkte lassen sich Wunschlisten hinzufĂĽgen (im Schema Wishlist genannt, mit den Feldern id, title und products), die wiederum einem Kunden zugeordnet sind (im Schema Customer genannt, mit den Feldern id, fullname und wishlists). Das JavaScript-Beispiel des Graphshops bietet also eine Katalog-Funktion: Produktdetails lassen sich abfragen und der Wunschliste eines Kunden zuordnen. Deshalb heiĂźt dieses Beispiel im folgenden Text Katalog-Service.
Die Java-Version erstellt eine GraphQL-API, in der sich Bestellungen (im Schema Order genannt, mit den Feldern id, deliveryaddress, price, deliverydate und isDelivered), sowie Adressen (im Schema Address genannt, mit den Feldern id und receiver) einem Kunden (im Schema Customer genannt, mit den Feldern id, fullname, addresses und orders) zuordnen lassen. Das Java-Beispiel des Graphshops bietet also eine Bestellungs-Funktion: Kunden können Details zu ihrer Bestellung und den hinterlegten Adressen abfragen. Deshalb heißt dieses Beispiel im folgenden Text Bestellungs-Service.
Für den Use Case des API-Gateways möchte die Marketingabteilung einen Vergleich zwischen den Wunschzetteln der Kunden und deren Bestellungen ableiten (auch wenn in diesem spezifischen Beispiel keine Produktdetails in den Bestellungen vorhanden sind – die Betrachtung bleibt daher rein hypothetisch). Da der Marketingabteilung technisches Know-how fehlt, benötigt sie einen einfachen, zentralen Zugriff auf die Daten. Daher soll ein API-Gateway zum Einsatz kommen, der die beiden Services zusammenführt und für die Marketingabteilung aufbereitet.