Erste Schritte mit GraphQL und Spring Boot

Erste Schritte mit GraphQL und Spring Boot

1. Einführung

GraphQL ist ein relativ neues Konzept von Facebook, das als Alternative zu REST für Web-APIs in Rechnung gestellt wird.

Dieser Artikel enthält eine Einführung in die Einrichtung eines GraphQL-Servers mit Spring Boot, damit dieser zu vorhandenen Anwendungen hinzugefügt oder in neuen Anwendungen verwendet werden kann.

2. Was ist GraphQL?

Herkömmliche REST-APIs arbeiten mit dem Konzept der vom Server verwalteten Ressourcen. Diese Ressourcen können gemäß den verschiedenen HTTP-Verben auf einige standardmäßige Arten bearbeitet werden. Dies funktioniert sehr gut, solange unsere API zum Ressourcenkonzept passt, fällt aber schnell auseinander, wenn wir davon abweichen müssen.

Dies leidet auch, wenn der Client Daten von mehreren Ressourcen gleichzeitig benötigt. Zum Beispiel das Anfordern eines Blogposts und der Kommentare. In der Regel wird dies dadurch gelöst, dass der Client entweder mehrere Anforderungen stellt oder der Server zusätzliche Daten bereitstellt, die möglicherweise nicht immer erforderlich sind, was zu größeren Antwortgrößen führt.

GraphQL offers a solution to both of these problems. Auf diese Weise kann der Client genau angeben, welche Daten gewünscht werden, einschließlich der Navigation durch untergeordnete Ressourcen in einer einzelnen Anforderung, und es können mehrere Abfragen in einer einzelnen Anforderung ausgeführt werden.

Es funktioniert auch viel rpc-artiger und verwendet benannte Abfragen und Mutationen anstelle eines obligatorischen Standardsatzes von Aktionen. This works to put the control where it belongs, with the API developer specifying what is possible, and the API consumer what is desired.

Ein Blog kann beispielsweise die folgende Abfrage zulassen:

query {
    recentPosts(count: 10, offset: 0) {
        id
        title
        category
        author {
            id
            name
            thumbnail
        }
    }
}

Diese Abfrage wird:

  • fordere die zehn neuesten Beiträge an

  • Fordern Sie für jeden Beitrag die ID, den Titel und die Kategorie an

  • für jede Postanfrage den Autor, der die ID, den Namen und das Vorschaubild zurückgibt

In einer herkömmlichen REST-API sind entweder 11 Anforderungen erforderlich - 1 für die Posts und 10 für die Autoren - oder die Autorendetails müssen in den Postdetails enthalten sein.

2.1. GraphQL-Schemata

Der GraphQL-Server stellt ein Schema zur Verfügung, das die API beschreibt. Dieses Schema besteht aus Typdefinitionen. Jeder Typ hat ein oder mehrere Felder, die jeweils null oder mehr Argumente annehmen und einen bestimmten Typ zurückgeben.

Das Diagramm setzt sich aus der Art und Weise zusammen, in der diese Felder miteinander verschachtelt sind. Beachten Sie, dass der Graph nicht azyklisch sein muss - Zyklen sind durchaus akzeptabel -, aber gerichtet. Das heißt, der Client kann von einem Feld zu seinen untergeordneten Feldern gelangen, aber nicht automatisch zum übergeordneten Feld zurückkehren, es sei denn, das Schema definiert dies explizit.

Ein Beispiel für ein GraphQL-Schema für ein Blog kann die folgenden Definitionen enthalten, die einen Beitrag, einen Autor des Beitrags und eine Stammabfrage beschreiben, um die neuesten Beiträge im Blog abzurufen.

type Post {
    id: ID!
    title: String!
    text: String!
    category: String
    author: Author!
}

type Author {
    id: ID!
    name: String!
    thumbnail: String
    posts: [Post]!
}

# The Root Query for the application
type Query {
    recentPosts(count: Int, offset: Int): [Post]!
}

# The Root Mutation for the application
type Mutation {
    writePost(title: String!, text: String!, category: String) : Post!
}

Das "!" Am Ende einiger Namen weist darauf hin, dass dies ein nicht nullfähiger Typ ist. Jeder Typ, der dies nicht hat, kann in der Antwort vom Server null sein. Der GraphQL-Dienst behandelt diese korrekt und ermöglicht es uns, untergeordnete Felder von nullfähigen Typen sicher anzufordern.

Der GraphQL-Dienst macht das Schema selbst auch mithilfe eines Standardsatzes von Feldern verfügbar, sodass jeder Client die Schemadefinition vorab abfragen kann.

Auf diese Weise kann der Client automatisch erkennen, wann sich das Schema ändert, und Clients können sich dynamisch an die Funktionsweise des Schemas anpassen. Ein unglaublich nützliches Beispiel hierfür ist das später diskutierte GraphiQL-Tool, mit dem wir mit jeder GraphQL-API interagieren können.

3. Einführung in GraphQL Spring Boot Starter

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short time. In Kombination mit derGraphQL Java Tools-Bibliothek müssen wir nur den für unseren Service erforderlichen Code schreiben.

3.1. Einrichten des Dienstes

Alles was wir dafür brauchen, sind die richtigen Abhängigkeiten:


    com.graphql-java
    graphql-spring-boot-starter
    5.0.2


    com.graphql-java
    graphql-java-tools
    5.2.4

Spring Boot nimmt diese automatisch auf und richtet die entsprechenden Handler so ein, dass sie automatisch funktionieren.

Standardmäßig wird dadurch der GraphQL-Dienst auf dem Endpunkt/graphqlunserer Anwendung verfügbar gemacht und POST-Anforderungen akzeptiert, die die GraphQL-Nutzlast enthalten. Dieser Endpunkt kann bei Bedarf in unsererapplication.properties-Datei angepasst werden.

3.2. Schema schreiben

Die GraphQL Tools-Bibliothek verarbeitet GraphQL Schema-Dateien, um die richtige Struktur zu erstellen, und verbindet dann spezielle Beans mit dieser Struktur. The Spring Boot GraphQL starter automatically finds these schema files.

Diese Dateien müssen mit der Erweiterung ".graphqls" gespeichert werden und können überall im Klassenpfad vorhanden sein. Wir können auch beliebig viele dieser Dateien haben, sodass wir das Schema nach Wunsch in Module aufteilen können.

Voraussetzung ist, dass es genau eine Root-Abfrage und maximal eine Root-Mutation gibt. Dies kann im Gegensatz zum Rest des Schemas nicht auf mehrere Dateien aufgeteilt werden. Dies ist eine Einschränkung der GraphQL-Schemadefinition selbst und nicht der Java-Implementierung.

3.3. Root Query Resolver

The root query needs to have special beans defined in the Spring context to handle the various fields in this root query. Im Gegensatz zur Schemadefinition gibt es keine Einschränkung, dass nur eine einzige Spring Bean für die Stammabfragefelder vorhanden ist.

Die einzigen Anforderungen sind, dass die BeansGraphQLQueryResolver implementieren und dass jedes Feld in der Stammabfrage aus dem Schema eine Methode in einer dieser Klassen mit demselben Namen hat.

public class Query implements GraphQLQueryResolver {
    private PostDao postDao;
    public List getRecentPosts(int count, int offset) {
        return postsDao.getRecentPosts(count, offset);
    }
}

Der Name der Methode muss in dieser Reihenfolge einer der folgenden sein:

  1. ist - nur wenn das Feld vom TypBoolean ist

  2. get

Die Methode muss Parameter haben, die beliebigen Parametern im GraphQL-Schema entsprechen, und kann optional einen endgültigen Parameter vom TypDataFetchingEnvironment. annehmen

Die Methode muss auch den richtigen Rückgabetyp für den Typ im GraphQL-Schema zurückgeben, wie wir gleich sehen werden. Beliebige einfache Typen -String, Int, List, usw. - kann mit den entsprechenden Java-Typen verwendet werden, und das System ordnet sie nur automatisch zu.

Das Obige definiert die MethodegetRecentPosts, die verwendet wird, um alle GraphQL-Abfragen für das FeldrecentPosts in dem zuvor definierten Schema zu verarbeiten.

3.4. Verwenden von Beans zur Darstellung von Typen

Every complex type in the GraphQL server is represented by a Java bean - ob aus der Stammabfrage oder von einer anderen Stelle in der Struktur geladen. Dieselbe Java-Klasse muss immer denselben GraphQL-Typ darstellen, der Name der Klasse ist jedoch nicht erforderlich.

Fields inside the Java bean will directly map onto fields in the GraphQL response based on the name of the field.

public class Post {
    private String id;
    private String title;
    private String category;
    private String authorId;
}

Alle Felder oder Methoden in der Java-Bean, die nicht dem GraphQL-Schema zugeordnet sind, werden ignoriert, verursachen jedoch keine Probleme. Dies ist wichtig, damit Feldauflöser funktionieren.

Beispielsweise entspricht das FeldauthorId hier nichts in unserem zuvor definierten Schema, kann jedoch für den nächsten Schritt verwendet werden.

3.5. Feldauflöser für komplexe Werte

Manchmal ist der Wert eines Feldes nicht einfach zu laden. Dies kann Datenbank-Lookups, komplexe Berechnungen oder anderes beinhalten. GraphQL Tools has a concept of a field resolver that is used for this purpose. Dies sind Spring Beans, die anstelle der Data Bean Werte liefern können.

Der Feldauflöser ist eine beliebige Bean im Spring-Kontext, die denselben Namen wie die Daten-Bean mit dem SuffixResolver hat und die SchnittstelleGraphQLResolver implementiert. Methoden auf der Field Resolver Bean folgen den gleichen Regeln wie auf der Data Bean, werden jedoch auch für die Data Bean selbst als erster Parameter bereitgestellt.

Wenn ein Field Resolver und die Data Bean Methoden für dasselbe GraphQL-Feld haben, hat der Field Resolver Vorrang.

public class PostResolver implements GraphQLResolver {
    private AuthorDao authorDao;

    public Author getAuthor(Post post) {
        return authorDao.getAuthorById(post.getAuthorId());
    }
}

Die Tatsache, dass diese Feldauflöser aus dem Spring-Kontext geladen werden, ist wichtig. Auf diese Weise können sie mit allen anderen Spring Managed Beans arbeiten, z. B. DAOs.

Wichtig ist,if the client does not request a field, then the GraphQL Server will never do the work to retrieve it. Dies bedeutet, dass, wenn ein Client einen Beitrag abruft und nicht nach dem Autor fragt, die oben beschriebene MethodegetAuthor()niemals ausgeführt wird und der DAO-Aufruf niemals ausgeführt wird.

3.6. Nullable Werte

Das GraphQL-Schema hat das Konzept, dass einige Typen nullfähig sind und andere nicht.

Dies kann im Java-Code durch direkte Verwendung von Nullwerten behandelt werden. Ebenso kann der neueOptional-Typ aus Java 8 hier direkt für nullfähige Typen verwendet werden, und das System wird mit den Werten das Richtige tun.

Dies ist sehr nützlich, da unser Java-Code offensichtlich dem GraphQL-Schema aus den Methodendefinitionen entspricht.

3.7. Mutationen

Bisher haben wir nur Daten vom Server abgerufen. GraphQL hat auch die Möglichkeit, die auf dem Server gespeicherten Daten durch Mutationen zu aktualisieren.

Aus Sicht des Codes gibt es keinen Grund, warum eine Abfrage keine Daten auf dem Server ändern kann. Wir könnten leicht Query Resolver schreiben, die Argumente akzeptieren, neue Daten speichern und diese Änderungen zurückgeben. Dies verursacht überraschende Nebenwirkungen für die API-Clients und wird als schlechte Praxis angesehen.

StattdessenMutations should be used to inform the client that this will cause a change to the data being stored.

Mutationen werden im Java-Code mithilfe von Klassen definiert, dieGraphQLMutationResolver anstelle vonGraphQLQueryResolver implementieren.

Ansonsten gelten dieselben Regeln wie für Abfragen. Der Rückgabewert aus einem Mutation-Feld wird dann genauso behandelt wie aus einem Query-Feld, sodass auch verschachtelte Werte abgerufen werden können.

public class Mutation implements GraphQLMutationResolver {
    private PostDao postDao;

    public Post writePost(String title, String text, String category) {
        return postDao.savePost(title, text, category);
    }
}

4. Einführung in GraphiQL

GraphQL hat auch ein Begleittool namens GraphiQL. Dies ist eine Benutzeroberfläche, die mit jedem GraphQL-Server kommunizieren und Abfragen und Mutationen ausführen kann. Eine herunterladbare Version davon existiert als Electron-App und kann vonhere abgerufen werden.

Es ist auch möglich, die webbasierte Version von GraphiQL automatisch in unsere Anwendung aufzunehmen, indem Sie die Abhängigkeit von GraphiQL Spring Boot Starter hinzufügen:


    com.graphql-java
    graphiql-spring-boot-starter
    5.0.2

Dies funktioniert nur, wenn wir unsere GraphQL-API auf dem Standardendpunkt/graphql hosten. Wenn dies nicht der Fall ist, wird die eigenständige Anwendung benötigt.

5. Zusammenfassung

GraphQL ist eine sehr aufregende neue Technologie, die möglicherweise die Entwicklung von Web-APIs revolutionieren kann.

Die Kombination aus dem Spring Boot GraphQL Starter und den GraphQL Java Tools-Bibliotheken macht es unglaublich einfach, diese Technologie zu neuen oder vorhandenen Spring Boot-Anwendungen hinzuzufügen.

Code-Schnipsel könnenover on GitHub gefunden werden.