Java-Anwendungen mit GraphQL, Teil 1

Das Schema: Beschreibung der API

Woher weiß der Client nun, welche Objekte und Felder es auf dem Server überhaupt gibt? Welche Queries er ausführen kann und welche Mutations mit welchen Parameter vorhanden sind? Dazu muss man die GraphQL API, die der Server zur Verfügung stellt, mit einem Schema beschreiben.

Das ist zwingend erforderlich. Je nach eingesetzter Programmiersprache und Framework gibt es dazu mehrere Möglichkeiten. Der gängigste Weg zur Beschreibung des Schemas ist mittlerweile die Schema Definition Language (SDL), die im Juni 2018 auch den Weg in die GraphQL-Spezifikation geschafft hat.

Ein Schema beschreibt die Objekte (in GraphQL Object Types genannt) mitsamt ihren Feldern, die Entwickler über die API abfragen können. Da GraphQL eine typsichere Sprache ist, muss man die Felder mit Typen versehen. Es gibt einige eingebaute skalare Typen, wie ID, String, Int, Boolean und außerdem Arrays beziehungsweise Listen. Die Definition eines eigenen, fachlichen Object Types könnte wie folgt aussehen:

type Rating {
id: ID!
beer: Beer!
author: User!
comment: String!
stars: Int!
}

Das Listing definiert zunächst den Typ Rating, der aus fünf Feldern besteht. Jedes Feld erfordert neben dem Namen den jeweiligen Typ. Dazu gehört auch, ob das Feld ein Pflichtfeld ist oder nicht (gekennzeichnet mit einem Ausrufezeichen) oder ob es sich um ein Array handelt (dann wäre der Feld-Typ mit eckigen Klammern umschlossen). Die Felder beer und author zeigen auf weitere Objekttypen.

Neben den Objekttypen besteht ein Schema noch aus Operationen, die Anwender über die API ausführen können – das sind die Queries, Mutations und Subscriptions.

In einem GraphQL-Schema heißen die Operationen Root Operation Types, die syntaktisch genauso wie Object Types in der SDL beschrieben sind und ebenfalls aus einer Menge an Feldern bestehen:

type Query {
beer(beerId: ID!): Beer
beers: [Beer!]!
}

type Mutation {
addRating(ratingInput: AddRatingInput): Rating!
login(username: String!): LoginResponse!
}

type Subscription {
onNewRating(beerId: ID!): Rating!
}

Am Query Type ist ein Feld beer definiert. Daran ist die Beschreibung von Argumenten für Felder zu sehen. Für Argumente müssen Entwickler einen Namen und den Typ angeben und festlegen, ob die Angabe des Arguments Pflicht ist.

Während Mutation und Subscription in einem Schema optional sind, ist zumindest die Definition des Query Type notwendig. Genau genommen können die drei Typen übrigens andere Namen tragen, in der Praxis ist es aber eine gute Idee, sich an die Namenskonventionen zu halten.

Die Root Operation Types sind bei einer Abfrage der Einstiegspunkt in eine API. Sie geht immer von einem Feld aus, das innerhalb eines der Root Operation Types definiert ist. Von dort aus kann dann weiter transitiv über alle referenzierten Types gewandert werden.

Bei der Ausführung einer Query stellt die GraphQL-Laufzeitumgebung sicher, dass Queries, die ein Client versendet, mit dem Schema kompatibel sind. Queries, die ungültig sind, weil sie zum Beispiel nicht existierende Felder abfragen, die gar nicht existieren, führt die Anwendung nicht aus. Ähnlich verhält es sich mit dem Ergebnis der Query: Der Client bekommt die Daten nur zurückgeschickt, wenn die Antwort dem Schema entspricht. Ebenfalls wird überprüft, ob Pflichtfelder wirklich befüllt sind und ob die zurückgegebenen Felder den erwarteten Typen entsprechen. Dadurch können sowohl Server als auch Client sicher sein, dass sie es nur mit syntaktisch korrekten Daten zu tun haben.

Introspection und Tooling

Das Schema einer GraphQL API kann zur Laufzeit eine reguläre GraphQL Query vom Server abfragen. Die Query heißt Introspection Query und ist mit der Reflection API von Java vergleichbar.

Die Möglichkeit, das Schema zur Laufzeit abzufragen, ermöglicht eine Vielzahl von Tools für das Arbeiten mit GraphQL APIs. Es gibt mit GraphiQL einen Browsereditor, mit dem Nutzer GraphQL Abfragen schreiben und ausführen können. GraphiQL fragt das jeweilige Schema einer API ab und bietet mit den Informationen Features wie Code-Completion und Syntax-Highlighting und -Validierung an. Den Editor können Entwickler übrigens in die eigene GraphQL-Anwendung integrieren, um zum Beispiel zur Entwicklungszeit Queries auszuprobieren.

Der API-Explorer GraphiQL (Abb. 3)

Neben dem Editor gibt es unter anderem ein GraphQL-Plug-in für IntelliJ IDEA sowie eine GraphQL-Erweiterung für Visual Studio Code. Mit beiden ist das Schreiben und Ausführen von Queries direkt in der IDE möglich – Code-Completion und Syntax-Checking im eigenen Sourcecode inklusive.

GraphQL Tooling in Visual Studio Code (Abb. 4)