© Atlas Agency/shutterstock.com
Daten mühelos zur Verfügung stellen

Ein GraphQL API mit ASP.NET Core


Das Bereitstellen von Daten über ein REST API ist in der Regel mit viel zusätzlichem Aufwand verbunden und oft technisch suboptimal. In diesem Artikel wird gezeigt, wie mit deutlich weniger Aufwand ein Web-Service-basiertes API mit GraphQL bereitgestellt werden kann, damit der Cliententwickler genau das bekommt, was er benötigt. Tauchen wir ein in eine attraktive API-Alternative für unser Backend.

Begeben wir uns zunächst einmal auf eine kleine Zeitreise ins Jahr 2012. Die Firma Facebook besitzt ein API für externe Entwickler, mit dem auf hochsensible Daten zugegriffen werden kann. Der zusätzliche Aufwand und die dahinterliegende Komplexität erschweren die Wartung und Weiterentwicklung. Ein neues Konzept wird intern vorgestellt und findet sofort großen Anklang, sodass sofort mit der Entwicklung dieser neuen Idee begonnen wird. Bereits im Jahr 2015 wird dann dazu eine Technical Preview für interne Teams und enge Partner ausgerollt. Das große Release folgt genau ein Jahr darauf – und ist die Geburtsstunde von GraphQL.

GraphQL ist eine Open-Source-Datenabfrage- und -Datenmanipulationssprache für das eigene API. Es bietet eine effiziente und flexible Alternative zu SQL, ganz im Sinne von REST und Ad-hoc-Web-Service-Architekturen. Es werden das Lesen, Schreiben und Abonnieren von Datenänderungen (Echtzeitupdates) unterstützt. Im Jahr 2018 wurde dann das GraphQL-Projekt an die Linux Foundation gespendet und die GraphQL Schema Definition Language (SDL) in die Spezifikation [1] aufgenommen. Bei GraphQL handelt es sich im Wesentlichen um die Standardspezifizierung der Abfragesprache und keine tatsächliche Implementierung, wobei Facebook selbstverständlich ein Referenzprojekt mit Node. js implementiert hat [2]. Von der Community getrieben, gibt es für alle gängigen Programmiersprachen eine GraphQL-Server-Implementierung: C#/.NET, Clojure, Elixir, Erlang, Go, Groovy, Java, JavaScript, Julia, Kotlin, Perl, PHP, Python, R, Ruby, Rust, Scala, Swift und OCaml/Reason [3].

REST vs. GraphQL

Um zu verstehen, wie GraphQL funktioniert, dient ein Vergleich mit klassischen RESTful Web Services als ideale Grundlage. Bei REST konzentriert sich das API-Design im Wesentlichen auf Ressourcen. Jede Ressource bekommt ihre eigene Schnittstelle und am besten soll dieses Designmodell selbstführend beschrieben werden (HATEOAS). Die Implementierung dieses Programmierparadigmas ist mit einigem Aufwand verbunden. Die Developer Experience für Konsumenten ist zusätzlich nicht gerade berauschend und technisch betrachtet auch oft suboptimal.

Sehen wir uns das bei einem kleinen Beispiel für ein Konferenz-API an. Das Ziel des Konsumenten ist die Abfrage nach einem Sprecher, seinen Vorträgen und den zugehörigen Bewertungen. In Abbildung 1 sehen wir das Beispiel dieser API-Kommunikation noch einmal verbildlicht.

biswanger_graphql_1.tif_fmt1.jpgAbb. 1: Die REST-API-Kommunikation

Wir sehen hier die klassischen Probleme mit Over- und Underfetching. Die erste Abfrage liefert uns nicht alle Daten, die für den weiteren Nutzen relevant sind (Underfetching) und zudem zu viele unnötige Informationen (Overfetching), wie zum Beispiel eine E-Mail-Adresse des Sprechers, obwohl wir nur dessen Namen benötigen. Für die weiteren benötigten Daten hangelt sich der Client durch zwei weitere Abfragen, weil bisher zu wenig notwendige Informationen geliefert wurden. Aus diesen Gründen sind häufig genutzte REST APIs, die nur intern im Unternehmen zum Einsatz kommen, etwas deutlicher maßgeschneidert. Der Autor bezeichnet sie eher als „pragmatische APIs“, die nicht vollwertig RESTful-kompatibel sind, was aber für den jeweiligen Einsatzzweck vollkommen legitim sein kann. Anders sieht es aus, wenn das API nicht zur internen Anwendung dient, sondern für Dritte bestimmt ist. Hier wird mit einem pragmatischen API eine extrem kontraproduktive Developer Experience geboten, die kein gutes Bild auf das Unternehmen wirft. Sehen wir uns daher eine alternative Möglichkeit mit einem GraphQL API an. In Abbildung 2 wird ein wesentlicher Vorteil sofort ersichtlich: Wir mussten nur einmal mit dem API kommunizieren, um alle notwendigen Informationen zu erhalten.

biswanger_graphql_2.tif_fmt1.jpgAbb. 2: Die GraphQL-API-Kommunikation

Das liegt daran, dass der Entwickler beim GraphQL API die Datenstruktur so definiert, wie er sie für seine Anfrage erhalten möchte – mit nur einem Query. Ein weiterer Vorteil ist die schnelle und einfache Implementierung dieses API für den Backend-Bereich, was wir gleich mit ASP.NET Core näher betrachten werden. Abbildung 3 zeigt einen Vergleich zwischen REST und GraphQL.

biswanger_graphql_3.tif_fmt1.jpgAbb. 3: REST vs. GraphQL

GraphQL Query und Mutation

Es wird unterschieden zwischen lesenden Operationen, den Queries, und schreibenden Operationen, den Mutations. Die Deklarationen zu Queries und Mutations sind JSON sehr ähnlich – mit dem Unterschied, dass man im Wesentlichen mit den Schlüsselwerten beschreibt, was zu erwarten ist. Für bestimmte Felder können Werte als Filter dienen. Bei einer Mutation sind natürlich die neuen Datenwerte ebenfalls enthalten. Für das soeben gezeigte Beispiel zur Abfrage eines Sprechers zeigt Abbildung 4 die zugehörige Query und wie der JSON-Datensatz im Ergebnis aussieht.

biswanger_graphql_4.tif_fmt1.jpgAbb. 4: GraphQL Query und das JSON-Ergebnis

Damit das GraphQL API seinen Job so ausüben kann, wie es eben veranschaulicht wurde, muss ihm auch unser gewünschtes Datenschema mit der zugehörigen Datenstruktur beigebracht werden. Sie basiert auf dem sogenannten GraphQL Type. GraphQL Types mit Queries und Mutations werden mit der Schema Definition Language (SDL) beschrieben und sind der Abfragedeklaration sehr ähnlich. Die SDL baut auf primitiven Datentypen auf. Sie beinhaltet Int, das für ein 32-Bit-Integer steht, Float, String in UTF-8, Boolean und eine ID als eindeutige Kennung. Listing 1 zeigt die Deklaration eines GraphQL-Typs, der unseren Sprecherdatensatz repräsentiert und einige der primitiven Datentypen nutzt.

Listing 1: GraphQL-Typen

type Speaker { speakerID: ID name: String ratings: Float talksCount: Int }

Eine Enumeration von Werten ist ebenfalls möglich, wie Listing 2 zeigt. Eine Liste von Datenwerten wird durch eckige Klammern deklariert. Ein Pflichtfeld wird über ein Ausrufezeichen nach der Typdeklaration festgelegt (Listing 3). Weitere detaillierte Informationen bezüglich der Typen gibt es in der offiziellen GraphQL-Dokumentation [4]. Die GraphQL-API-Serverlösung für .NET Core hilft uns automatisch dabei, unsere .NET-Klassen in SDL-Typen zu beschreiben, wie wir gleich sehen werden.

Listing 2: Enumeration von Typen

enum tropic { DOTNET MONGODB GRAPHQL JAVASCRIPT TYPESCRIPT ANGULAR }

Listing 3: Non-nullable Types

type Speaker { speakerID: ID! name: String! ratings: Float talksCount: Int }

Das Sche...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang