Premiers pas avec GraphQL et Spring Boot

Premiers pas avec GraphQL et Spring Boot

1. introduction

GraphQL est un concept relativement nouveau de Facebook qui est présenté comme une alternative à REST pour les API Web.

Cet article présente une introduction à la configuration d’un serveur GraphQL à l’aide de Spring Boot afin que celui-ci puisse être ajouté à des applications existantes ou utilisé dans de nouvelles.

2. Qu'est-ce que GraphQL?

Les API REST traditionnelles fonctionnent avec le concept de ressources gérées par le serveur. Ces ressources peuvent être manipulées de manière standard, en suivant les différents verbes HTTP. Cela fonctionne très bien tant que notre API correspond au concept de ressource, mais s'effondre rapidement lorsque nous devons en dévier.

Cela se produit également lorsque le client a besoin de données provenant de plusieurs ressources en même temps. Par exemple, demander un article de blog et les commentaires. Cela est généralement résolu en faisant en sorte que le client fasse plusieurs demandes ou que le serveur fournisse des données supplémentaires qui ne sont pas toujours nécessaires, ce qui entraîne une taille de réponse plus grande.

GraphQL offers a solution to both of these problems. Il permet au client de spécifier exactement les données souhaitées, notamment en naviguant dans les ressources enfants dans une seule requête, et autorise plusieurs requêtes dans une seule requête.

Il fonctionne également d'une manière beaucoup plus RPC, en utilisant des requêtes et des mutations nommées au lieu d'un ensemble d'actions obligatoire standard. This works to put the control where it belongs, with the API developer specifying what is possible, and the API consumer what is desired.

Par exemple, un blog peut autoriser la requête suivante:

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

Cette requête va:

  • demander les dix derniers messages

  • pour chaque article, demandez l'ID, le titre et la catégorie

  • pour chaque message, demandez à l'auteur en renvoyant l'ID, le nom et la vignette

Dans une API REST traditionnelle, cela nécessite soit 11 demandes (1 pour les publications et 10 pour les auteurs), soit l'inclusion des détails de l'auteur dans les détails de la publication.

2.1. Schémas GraphQL

Le serveur GraphQL expose un schéma décrivant l'API. Ce schéma est composé de définitions de types. Chaque type comporte un ou plusieurs champs, qui prennent chacun zéro ou plusieurs arguments et renvoient un type spécifique.

Le graphique est constitué de la manière dont ces champs sont imbriqués les uns dans les autres. Notez qu'il n'est pas nécessaire que le graphique soit acyclique - les cycles sont parfaitement acceptables - mais il est dirigé. Autrement dit, le client peut passer d’un champ à ses enfants, mais il ne peut pas revenir automatiquement au parent à moins que le schéma ne le définisse explicitement.

Un exemple de schéma GraphQL pour un blog peut contenir les définitions suivantes, décrivant une publication, un auteur de la publication et une requête racine pour obtenir les publications les plus récentes sur le blog.

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!
}

Le «!» À la fin de certains noms indique qu'il s'agit d'un type non nullable. Tout type n'ayant pas cela peut être null dans la réponse du serveur. Le service GraphQL les gère correctement, ce qui nous permet de demander des champs enfants de types nullables en toute sécurité.

Le service GraphQL expose également le schéma lui-même en utilisant un ensemble standard de champs, permettant à tout client d’interroger la définition du schéma à l’avance.

Cela peut permettre au client de détecter automatiquement quand le schéma est modifié et aux clients qui s'adaptent de manière dynamique à la façon dont le schéma fonctionne. L’outil GraphiQL, dont nous parlerons plus loin, est un exemple extrêmement utile. Il nous permet d’interagir avec n’importe quelle API GraphQL.

3. Présentation de GraphQL Spring Boot Starter

The Spring Boot GraphQL Starter offers a fantastic way to get a GraphQL server running in a very short time. Combiné avec la bibliothèqueGraphQL Java Tools, nous n'avons besoin que d'écrire le code nécessaire à notre service.

3.1. Mise en place du service

Tout ce dont nous avons besoin pour que cela fonctionne est les dépendances correctes:


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


    com.graphql-java
    graphql-java-tools
    5.2.4

Spring Boot les récupérera automatiquement et configurera les gestionnaires appropriés pour qu'ils fonctionnent automatiquement.

Par défaut, cela exposera le service GraphQL sur le point de terminaison/graphql de notre application et acceptera les requêtes POST contenant la charge utile GraphQL. Ce point de terminaison peut être personnalisé dans notre fichierapplication.properties si nécessaire.

3.2. Ecrire le schéma

La bibliothèque GraphQL Tools traite les fichiers de schéma GraphQL pour créer la structure appropriée, puis connecte des beans spéciaux à cette structure. The Spring Boot GraphQL starter automatically finds these schema files.

Ces fichiers doivent être enregistrés avec l'extension «.graphqls» et peuvent être présents n'importe où sur le chemin de classe. Nous pouvons également avoir autant de fichiers que vous le souhaitez, nous pouvons donc scinder le schéma en modules à votre guise.

La première condition est qu'il doit exister exactement une requête racine et jusqu'à une mutation racine. Cela ne peut pas être divisé en fichiers, contrairement au reste du schéma. Il s'agit d'une limitation de la définition de schéma GraphQL elle-même et non de l'implémentation Java.

3.3. Résolveur de requête racine

The root query needs to have special beans defined in the Spring context to handle the various fields in this root query. Contrairement à la définition de schéma, il n'y a pas de restriction à ce qu'il n'y ait qu'un seul bean Spring pour les champs de requête racine.

Les seules exigences sont que les beans implémententGraphQLQueryResolver et que chaque champ de la requête racine du schéma ait une méthode dans l'une de ces classes avec le même nom.

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

Les noms de la méthode doivent être l’un des suivants, dans cet ordre:

  1. est - uniquement si le champ est de typeBoolean

  2. obtenir

La méthode doit avoir des paramètres qui correspondent à tous les paramètres du schéma GraphQL, et peut éventuellement prendre un paramètre final de typeDataFetchingEnvironment.

La méthode doit également renvoyer le type de retour correct pour le type dans le schéma GraphQL, comme nous allons le voir. Tous les types simples -String, Int, List, etc. - peut être utilisé avec les types Java équivalents, et le système les mappe automatiquement.

Ce qui précède a défini la méthodegetRecentPosts qui sera utilisée pour gérer toutes les requêtes GraphQL pour le champrecentPosts dans le schéma défini précédemment.

3.4. Utiliser des haricots pour représenter des types

Every complex type in the GraphQL server is represented by a Java bean - qu'il soit chargé à partir de la requête racine ou de n'importe où ailleurs dans la structure. La même classe Java doit toujours représenter le même type GraphQL, mais le nom de la classe n'est pas nécessaire.

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;
}

Tous les champs ou méthodes du bean Java qui ne sont pas mappés sur le schéma GraphQL seront ignorés, mais ne poseront pas de problèmes. Ceci est important pour que les résolveurs de champ fonctionnent.

Par exemple, le champauthorId ici ne correspond à rien de notre schéma que nous avons défini précédemment, mais il sera disponible pour être utilisé pour l'étape suivante.

3.5. Résolveurs de champ pour les valeurs complexes

Parfois, la valeur d’un champ n’est pas triviale à charger. Cela peut impliquer des recherches dans la base de données, des calculs complexes ou autre chose. GraphQL Tools has a concept of a field resolver that is used for this purpose. Ce sont des beans Spring qui peuvent fournir des valeurs à la place du bean de données.

Le résolveur de champ est n'importe quel bean dans le contexte Spring qui a le même nom que le bean de données, avec le suffixeResolver, et qui implémente l'interfaceGraphQLResolver. Les méthodes du bean résolveur de champ suivent les mêmes règles que le bean de données, mais le bean de données lui-même est également fourni en tant que premier paramètre.

Si un résolveur de champ et le bean de données ont tous deux des méthodes pour le même champ GraphQL, le résolveur de champ aura priorité.

public class PostResolver implements GraphQLResolver {
    private AuthorDao authorDao;

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

Le fait que ces résolveurs de champ soient chargés à partir du contexte Spring est important. Cela leur permet de travailler avec tout autre bean géré par Spring - par exemple, les DAO.

Surtout,if the client does not request a field, then the GraphQL Server will never do the work to retrieve it. Cela signifie que si un client récupère une publication et ne demande pas l'auteur, la méthodegetAuthor() ci-dessus ne sera jamais exécutée et l'appel DAO ne sera jamais effectué.

3.6. Valeurs nulles

Le schéma GraphQL a le concept que certains types sont nullables et d'autres non.

Cela peut être géré dans le code Java en utilisant directement des valeurs nulles, mais également, le nouveau typeOptional de Java 8 peut être utilisé directement ici pour les types Nullable, et le système fera la bonne chose avec les valeurs.

Ceci est très utile car cela signifie que notre code Java est plus clairement identique au schéma GraphQL des définitions de méthodes.

3.7. Mutations

Jusqu'à présent, tout ce que nous avons fait consistait à récupérer des données du serveur. GraphQL a également la possibilité de mettre à jour les données stockées sur le serveur, au moyen de mutations.

Du point de vue du code, il n'y a aucune raison pour qu'une requête ne puisse pas modifier les données sur le serveur. Nous pourrions facilement écrire des résolveurs de requêtes acceptant les arguments, enregistrer de nouvelles données et renvoyer ces modifications. Cela entraînera des effets secondaires surprenants pour les clients API, ce qui est considéré comme une mauvaise pratique.

Au lieu de cela,Mutations should be used to inform the client that this will cause a change to the data being stored.

Les mutations sont définies dans le code Java à l'aide de classes qui implémententGraphQLMutationResolver au lieu deGraphQLQueryResolver.

Sinon, toutes les règles sont les mêmes que pour les requêtes. La valeur de retour d'un champ Mutation est alors traitée exactement de la même manière que celle d'un champ de requête, ce qui permet également d'extraire des valeurs imbriquées.

public class Mutation implements GraphQLMutationResolver {
    private PostDao postDao;

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

4. Présentation de GraphiQL

GraphQL a également un outil compagnon appelé GraphiQL. Il s'agit d'une interface utilisateur capable de communiquer avec n'importe quel serveur GraphQL et d'exécuter des requêtes et des mutations sur celui-ci. Une version téléchargeable de celui-ci existe en tant qu'application Electron et peut être récupérée à partir dehere.

Il est également possible d'inclure automatiquement la version Web de GraphiQL dans notre application, en ajoutant la dépendance GraphiQL Spring Boot Starter:


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

Cela ne fonctionnera que si nous hébergeons notre API GraphQL sur le point de terminaison par défaut de/graphql, donc l'application autonome sera nécessaire si ce n'est pas le cas.

5. Sommaire

GraphQL est une nouvelle technologie très intéressante qui peut potentiellement révolutionner la façon dont les API Web sont développées.

La combinaison des bibliothèques Spring Boot GraphQL Starter et GraphQL Java Tools facilite l’ajout de cette technologie à toutes les applications Spring Boot existantes ou nouvelles.

Des extraits de code peuvent être trouvésover on GitHub.