Notre taverne GraphQL fonctionne désormais parfaitement.
Les aventuriers consultent les quêtes disponibles, les nouveaux arrivants s'inscrivent au registre, et le schéma GraphQL est généré automatiquement par Quarkus à partir du code Java.
Mais dans les grands royaumes, une taverne ne reste jamais isolée très longtemps.
Le panneau d'affichage des quêtes dans la ville voisine veut récupérer la liste des aventuriers.
La guilde des mages souhaite consulter les contrats actifs avant d'envoyer ses apprentis.
Une autre application doit désormais consommer notre API GraphQL.
Exposer un schéma n'est donc que la moitié du voyage. L'autre moitié consiste à apprendre à interroger une API GraphQL distante depuis Quarkus, grâce au client proposé par SmallRye GraphQL.
La taverne devient cliente
Jusqu'ici, notre établissement servait les informations. Les aventuriers se présentaient au comptoir, posaient leurs questions, et repartaient avec exactement ce qu'ils avaient demandé.
Mais une rumeur circule dans le royaume : la guilde des aventuriers souhaite ouvrir une antenne dans la ville voisine, une petite échoppe chargée de consulter le registre de la taverne principale à distance, sans en dupliquer le contenu, sans en reproduire la logique.
Cette antenne ne possède ni base de données ni règles métier complexes. Elle agit simplement comme un messager capable de dialoguer avec l'API GraphQL exposée par la taverne, de récupérer les informations du registre et de les transmettre à qui en a besoin.
Pour équiper ce messager, il faut ajouter l'extension quarkus-smallrye-graphql-client :
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-graphql-client</artifactId>
</dependency>
Cette extension permet à Quarkus de générer des clients GraphQL capables d'exécuter des Query et des Mutation exactement comme un frontend ou une autre application le ferait, sans que le développeur ait à construire les requêtes à la main.
Réutiliser le schéma généré par la taverne
Dans l'article précédent, SmallRye GraphQL avait automatiquement produit un schéma à partir des records Java de la taverne :
"Données publiques d'un aventurier"
type AventurierResponse {
"Classe (ex: Guerrier, Mage)"
classe: String!
"Identifiant unique"
id: BigInteger!
"Niveau d'expérience"
niveau: Int!
"Nom de l'aventurier"
nom: String!
"Liste des quêtes en cours ou terminées"
quetes: [QueteResponse!]!
}
"Mutation root"
type Mutation {
"Ajoute un nouvel aventurier dans la taverne."
ajouterAventurier(input: AventurierInput!): AventurierResponse!
}
"Query root"
type Query {
"Recherche un aventurier par son identifiant unique."
aventurier(id: BigInteger!): AventurierResponse
"Liste tous les aventuriers présents dans la taverne."
aventuriers: [AventurierResponse!]!
}
"Détails d'une quête"
type QueteResponse {
"Niveau de difficulté"
difficulte: String
"Identifiant de la quête"
id: BigInteger!
"Montant de la prime"
recompenseOr: Int
"Intitulé"
titre: String!
}
"Données pour la création d'un aventurier"
input AventurierInput {
"Classe choisie"
classe: String!
"Niveau initial (doit être > 0)"
niveau: Int!
"Nom de l'aventurier"
nom: String!
}notre schéma
Ce schéma n'est pas seulement une documentation affichée sur une page. C'est le véritable contrat entre les royaumes, le parchemin officiel que toute application cliente peut consulter pour comprendre exactement quelles opérations sont disponibles, quels champs existent, lesquels sont obligatoires, et comment naviguer entre les objets.
Notre antenne de guilde va s'appuyer sur ce contrat pour savoir quoi demander et sous quelle forme recevoir les réponses, sans jamais avoir besoin de connaître les cuisines de la taverne.
Créer les modèles côté client
La guilde des aventuriers ne s'intéresse pas à toute la richesse du registre. Elle veut simplement connaître le nom des aventuriers, leur niveau et les quêtes qu'ils transportent. Plutôt que de reproduire fidèlement l'intégralité des modèles de la taverne, l'antenne se contente de déclarer les champs dont elle a réellement besoin :
public class Aventurier {
private Long id;
private String nom;
private String classe;
private Integer niveau;
private List<Quete> quetes;
// getters et setters
}
public class Quete {
private String titre;
private Integer recompenseOr;
// getters et setters
}
C'est l'un des avantages les plus discrets de GraphQL : le client ne déclare que ce qu'il souhaite manipuler.
Si demain la taverne ajoute de nouveaux champs à ses aventuriers, l'antenne n'en saura rien et n'en souffrira pas. Elle continue de recevoir exactement ce qu'elle a demandé, ni plus, ni moins.
Déclarer un client GraphQL typesafe
L'antenne dispose maintenant de ses modèles. Il lui faut un guichet pour passer ses commandes à la taverne distante. Ce guichet prend la forme d'une simple interface Java :
@GraphQLClientApi(configKey = "taverne")
public interface TaverneClientApi {
List<Aventurier> aventuriers();
}
première version du client
Cette interface ressemble beaucoup au MicroProfile REST Client, mais chaque méthode représente ici une Query GraphQL. Lorsque aventuriers() sera appelée, SmallRye GraphQL construira automatiquement la requête GraphQL correspondante, l'enverra à la taverne distante et transformera la réponse en objets Java.
Le client devient un proxy entre les deux établissements, un intermédiaire discret qui connaît le langage des deux côtés du comptoir.
Configurer l'adresse de la taverne distante
Avant d'envoyer ses messagers, l'antenne doit savoir où se trouve la taverne principale. Une seule ligne dans le fichier de configuration suffit :
quarkus.smallrye-graphql-client.taverne.url=http://localhost:8080/graphql
Le nom taverne correspond directement au configKey déclaré dans l'annotation @GraphQLClientApi.
À partir de là, Quarkus sait exactement où diriger les requêtes lorsque le client est injecté dans l'application.
Interroger la taverne depuis un endpoint REST
Pour vérifier que les messagers font bien leur travail, nous allons exposer un endpoint REST depuis l'antenne :
@Path("/guilde")
public class GuildeResource {
@Inject
TaverneClientApi taverneClient;
@GET
@Path("/aventuriers")
@Produces(MediaType.APPLICATION_JSON)
public List<Aventurier> recupererAventuriers() {
return taverneClient.aventuriers();
}
}
Lorsque ce endpoint est appelé, l'antenne contacte automatiquement la taverne, exécute la Query aventuriers, reçoit les résultats et les transmet au client HTTP sous forme de liste Java.
Le développeur manipule des objets fortement typés, sans jamais toucher à une requête GraphQL brute ni à une sérialisation manuelle.
Toute la complexité du voyage entre les deux royaumes reste invisible, rangée derrière le comptoir.
Ce que le client GraphQL génère réellement
Lorsque la méthode taverneClient.aventuriers() est appelée, SmallRye GraphQL ne se contente pas de lancer un appel HTTP générique.
Il inspecte les modèles Java déclarés par l'antenne et construit une requête GraphQL précise qui ne demande que les champs réellement utilisés :
query {
aventuriers {
id
nom
classe
niveau
quetes {
titre
recompenseOr
}
}
}
C'est là que le schéma généré par la taverne prend toute sa valeur.
Le client s'appuie sur ce contrat pour savoir quels champs sont accessibles, comment les objets sont liés, et quelles règles de nullabilité s'appliquent.
Le développeur n'écrit pratiquement aucune sérialisation manuelle : le framework s'en charge, du premier champ au dernier.
Ajouter des paramètres aux Query
La taverne exposait également une Query permettant de retrouver un aventurier précis par son identifiant. L'antenne peut consommer cette opération de la même façon, en ajoutant simplement la méthode correspondante à l'interface :
@GraphQLClientApi(configKey = "taverne")
public interface TaverneClientApi {
List<Aventurier> aventuriers();
Aventurier aventurier(Long id);
}
client version 2
SmallRye GraphQL prendra en charge le mapping du paramètre Java vers l'argument GraphQL et générera automatiquement une requête semblable à :
query {
aventurier(id: 1) {
nom
niveau
quetes {
titre
}
}
}
Le messager sait désormais poser des questions précises, pas seulement demander la liste complète du registre.
Consommer une Mutation
L'antenne ne se contente pas de lire le registre.
Elle peut aussi y inscrire de nouveaux aventuriers, en envoyant une Mutation vers la taverne distante. Il suffit de déclarer le modèle d'entrée côté client :
public class AventurierInput {
private String nom;
private String classe;
private Integer niveau;
// getters et setters
}
Puis d'ajouter l'opération à l'interface :
@GraphQLClientApi(configKey = "taverne")
public interface TaverneClientApi {
List<Aventurier> aventuriers();
Aventurier aventurier(Long id);
Aventurier ajouterAventurier(AventurierInput input);
}
client version 3
Lorsque le messager se présente au comptoir avec le formulaire d'Eldric :
AventurierInput input = new AventurierInput();
input.setNom("Eldric");
input.setClasse("Mage");
input.setNiveau(18);
Aventurier aventurier = taverneClient.ajouterAventurier(input);
SmallRye GraphQL traduit automatiquement cet appel Java en Mutation GraphQL :
mutation {
ajouterAventurier(input: {
nom: "Eldric"
classe: "Mage"
niveau: 18
}) {
id
nom
classe
}
}
L'antenne parle désormais exactement le même langage que la taverne.
Eldric est inscrit au registre principal sans que l'antenne ait jamais eu à connaître les détails du grand livre.
Observer les échanges entre tavernes
Lorsqu'un problème survient entre deux royaumes, il devient souvent nécessaire d'inspecter les messages échangés sur la route. SmallRye GraphQL permet d'afficher dans la console les requêtes et réponses générées automatiquement, comme un espion posté au bord du chemin qui note chaque parole prononcée entre les messagers :
quarkus.log.category."io.smallrye.graphql.client".level=TRACE
quarkus.log.category."io.smallrye.graphql.client".min-level=TRACE
Avec ce niveau de log activé, chaque requête GraphQL envoyée par le client apparaît directement dans la console.
C'est particulièrement utile pour comprendre ce qui est réellement généré derrière les méthodes Java, et pour diagnostiquer rapidement un désaccord entre les deux établissements.
2026-04-29 09:04:32,429 TRACE [] (executor-thread-1) full graphql request: {"query":"query aventuriers { aventuriers {id nom classe niveau quetes {titre recompenseOr}} }","variables":{},"operationName":"aventuriers"}
2026-04-29 09:04:32,642 TRACE [] (executor-thread-1) response graphql: {"data":{"aventuriers":[{"id":1,"nom":"Baldric","classe":"Guerrier","niveau":12,"quetes":[{"titre":"Nettoyer les caves infestées","recompenseOr":250}]}]}}extrait de logs
Générer automatiquement le client à partir du schéma
Dans cet article, l'antenne reconstruit manuellement les modèles Java dont elle a besoin.
Cette approche reste la plus courante avec Quarkus : légère, explicite et très lisible.
Mais SmallRye GraphQL propose également un générateur expérimental capable de produire automatiquement des modèles et des clients à partir :
- du schéma GraphQL exposé par la taverne ;
- et des requêtes
.graphqlréellement utilisées par l'application cliente.
Le fonctionnement se rapproche des outils de génération OpenAPI, avec une différence importante : en GraphQL, le schéma décrit un graphe de données potentiellement immense.
Le générateur a donc besoin de connaître les Query et Mutation réellement consommées afin de produire uniquement les modèles utiles au client.
Le schéma devient alors un véritable contrat partagé entre les royaumes.
Conclusion
Notre taverne a appris deux langages au fil de ces deux articles.
Dans le premier, elle apprenait à parler : exposer un schéma GraphQL, tenir un registre auto-documenté, répondre aux aventuriers avec précision.
Dans celui-ci, elle a appris à écouter : interroger une API GraphQL distante, transformer automatiquement les réponses en objets Java fortement typés, envoyer des Mutation sans écrire une seule requête à la main.
Quelques modèles Java, une interface annotée, une URL de configuration : c'est tout ce dont l'antenne avait besoin pour dialoguer avec la taverne principale. Quarkus et SmallRye GraphQL ont pris en charge la génération des requêtes, les appels réseau, la sérialisation et le mapping des réponses.
Le comptoir GraphQL ne sert donc pas uniquement à répondre aux aventuriers. Il permet aussi aux tavernes du royaume de se parler entre elles, avec un langage commun, structuré et fortement typé, bien plus souple qu'une longue tournée de routes REST spécialisées.
Tout le code relatif à cet article est consultable ici :