> ## Content Index
> Fetch the complete content index at: https://www.sfeir.dev/llms.txt
> Use this file to discover other available public pages before exploring further.

# Qute : le scribe du donjon pour afficher la taverne avec Quarkus
- URL: https://www.sfeir.dev/back/qute-le-scribe-du-donjon-pour-afficher-la-taverne-avec-quarkus/
- Published: 2026-06-25T08:47:01.000Z
- Updated: 2026-06-25T08:47:01.000Z
- Description: Dans une taverne bien tenue, chaque parchemin compte. Avec Quarkus, Qute assemble les pages comme un scribe méticuleux : rapide, fiable et vérifié à la compilation. Découvrez comment structurer vos templates, éviter les pièges et servir des pages aussi propres qu’un registre de guilde.
- Author: Erwan Le Tutour
- Tags: Back, Java, quarkus

Dans la salle commune, le feu crépite.  
Le tavernier déroule un parchemin : ce n’est pas la carte d’un donjon, mais la mise en page du menu.  
Chaque salle, chaque porte, chaque recoin doit s’afficher sans trembler… même en pleine bataille.

Quand on bâtit une application web avec [Quarkus](https://www.sfeir.dev/quarkus-definition/), **Qute** joue ce rôle de scribe :  
il assemble les fragments, injecte les données, et produit des pages claires, rapides, prévisibles.

Cet article fait le tour complet : présentation, comparaison, cas d’usage et exemples concrets.

## Présentation de Qute

Qute est le moteur de templates natif de Quarkus.  
Il est pensé pour la performance, la compilation à l’avance, et une intégration directe avec Java.

Dans notre taverne, les pages sont servies par un contrôleur qui expose des **templates typés** via `@CheckedTemplate` :

```java
@CheckedTemplate(basePath = "")
public static class Templates {
    public static native TemplateInstance index(String title);
    public static native TemplateInstance drinks(String title, List<Drink> drinks);
    public static native TemplateInstance food(String title, List<Food> food);
    public static native TemplateInstance booking(String title, BookingForm form, Map<String, String> errors);
    public static native TemplateInstance bookingSuccess(String title, Booking booking);
    public static native TemplateInstance admin(String title, List<Booking> bookings);
}

```

Ici, la classe interne `Templates` agit comme un **catalogue typé** de pages.  
Chaque méthode correspond à un template et définit les données attendues, ce qui rend les erreurs visibles dès la compilation.

Avec ce mécanisme, Quarkus vérifie **à la compilation** que les paramètres passés aux templates sont bien conformes.  
On évite ainsi les fautes de frappe sur les noms de variables en production.

Qute s’appuie sur l’écosystème Quarkus sans friction.  
Le module inclut les extensions dédiées au rendu de templates côté REST :

```xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-qute</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-rest-qute</artifactId>
</dependency>

```

Ces dépendances suffisent pour brancher Qute sur des endpoints JAX-RS et rendre des pages HTML.

En clair :

- `quarkus-qute` : moteur de templating.
- `quarkus-rest-qute` : intégration avec JAX-RS.

## Qute vs Thymeleaf

[Thymeleaf](https://www.sfeir.dev/back/integration-de-thymeleaf-dans-une-application-spring-boot/) est un choix historique dans l’écosystème Spring.  
Qute, lui, est pensé pour **Quarkus** et pour des builds rapides et prévisibles.

### Points communs

- Ce sont deux moteurs de templates côté serveur : on prépare les données en Java et on rend du HTML.
- Ils favorisent une séparation claire entre logique métier et présentation.
- On peut réutiliser des layouts et structurer les pages avec des fragments/partials.

### Points de divergence

| Critère     | Qute (Quarkus)                        | Thymeleaf (Spring)                  |
| ----------- | ------------------------------------- | ----------------------------------- |
| Validation  | Templates vérifiés à la compilation   | Validation surtout à l’exécution    |
| Intégration | Modèle Java direct, peu d’abstraction | Modèle Spring centré sur le runtime |
| Performance | Très léger, peu de réflexion          | Plus riche, plus de coûts runtime   |
| DX          | Typage fort via @CheckedTemplate      | Souple mais moins strict            |
| Cas d’usage | Quarkus natif                         | Applications Spring                 |

### Extension : Qute vs Vaadin

[Vaadin](https://www.sfeir.dev/back/vaadin-definition/) ne joue pas dans la même cour : c’est un framework UI complet.  
Il génère l’interface depuis Java (composants) et s’appuie sur un rendu côté client/serveur.

| Critère                | Qute (templating)    | Vaadin (UI framework)   |
| ---------------------- | -------------------- | ----------------------- |
| Approche               | HTML + données       | Composants Java         |
| Courbe d’apprentissage | Faible               | Plus élevée             |
| Contrôle HTML/CSS      | Très direct          | Abstraction forte       |
| Cas d’usage            | Pages web classiques | Applications web riches |

#### Quand choisir quoi ?

- **Choisir Qute** si l’objectif est de rendre des pages classiques (pages publiques, formulaires simples, backoffice léger) avec un contrôle fin sur le HTML/CSS.
- **Choisir Vaadin** si l’on vise une application web riche, type “outil interne”, avec beaucoup d’interactions et un UI piloté majoritairement en Java.
- **Choisir Thymeleaf** si l’écosystème Spring est déjà en place et que l’on souhaite rester sur une approche serveur classique.

**Décision du tavernier**  
Pour les annonces, les menus, et le registre des chambres, **Qute** suffit largement : rapide, lisible, et fidèle au parchemin.  
Pour un “tableau de bord de guilde” rempli de filtres, de drag & drop et de widgets interactifs, **Vaadin** prend l’avantage.  
Et si la guilde est déjà jurée à Spring, **Thymeleaf** reste l’option la plus naturelle.

Dans notre taverne, cela se traduit par moins de surprises et un service rapide, même quand l’auberge déborde.

## Exemples (templates, tags, validation, bean mapping)

### Template principal avec tag personnalisé

Le layout partagé est défini dans un tag Qute (`main.html`).  
Toutes les pages viennent s’y ancrer grâce à `{#main ...}` :

```html
{#main title=title}
    <p style="text-align: center; font-size: 1.3rem; line-height: 1.6;">
        Bienvenue à <strong>The Falling Whale</strong>, voyageur ! <br>
        Que vous veniez d'un autre royaume ou d'une autre galaxie, <br>
        notre établissement est l'endroit idéal pour poser votre serviette.
    </p>
{/main}

```

le template principal

Ce bloc montre une page “enfant” qui se contente de fournir son contenu.  
Le tag `main` se charge d’encadrer la page et d’afficher le titre.

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/image-8.png)

notre page d'acceuil

Le tag, lui, définit la structure globale (navigation, parchemin, footer) :

```html
<div class="parchment">
    <header>
        <h1>The Falling Whale</h1>
        <h2 style="font-family: 'MedievalSharp'; font-size: 1.5rem; color: #8b4513; margin-top: 0.5rem;">{title}</h2>
    </header>
    
    {nested-content}

    <div class="wax-seal">42</div>
</div>

```

Ici, `{nested-content}` injecte le contenu de la page enfant dans le layout.  
Le rendu final donne une page cohérente, avec un style partagé sur tout le site.

### Boucles et rendu de listes

Qute s’appuie sur une syntaxe claire pour parcourir une collection.  
Exemple avec la carte des boissons :

```html
{#for drink in drinks}
<div style="margin-bottom: 2rem;">
    <div style="display: flex; justify-content: space-between; font-family: 'MedievalSharp'; font-size: 1.4rem;">
        <span>{drink.name}</span>
        <span>{drink.price} PO</span>
    </div>
    <p style="font-style: italic; margin-top: 5px;">{drink.description}</p>
</div>
{/for}

```

La boucle `#for` itère sur `drinks` et injecte chaque boisson dans la carte.  
Le rendu final donne une page lisible, avec un prix aligné à droite.

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/image-9.png)

la carte des boissons

### Validation côté formulaire

Le formulaire de réservation est validé via un bean dédié, annoté avec Jakarta Validation :

```java
public class BookingForm {
    @RestForm
    @NotBlank(message = "Le nom est obligatoire.")
    @Size(max = 100, message = "Le nom est trop long (100 caractères max).")
    public String adventurerName;

    @RestForm
    @NotBlank(message = "La date d'arrivée est obligatoire.")
    public String arrivalDate;

    @RestForm
    @Min(value = 1, message = "Le nombre de nuits doit être au moins 1.")
    @Max(value = 10, message = "La durée du sejour ne doit pas dépasser 10 nuits.")
    public int nights = 1;

    @RestForm
    @NotBlank(message = "Le type de suite est obligatoire.")
    public String roomType;
}

```

Chaque champ du formulaire est annoté pour imposer les règles métier.  
Le contrôleur s’appuie ensuite sur ces contraintes pour afficher des erreurs propres dans la page de réservation.

Et côté template, les messages sont rendus proprement :

```html
{#if errors and !errors.isEmpty}
<div class="error-box">
    <strong>Oups, il y a quelques problèmes :</strong>
    <ul style="margin: 0.5rem 0 0 1.2rem;">
        {#for e in errors.values}
        <li>{e}</li>
        {/for}
    </ul>
</div>
{/if}

```

Le bloc `#if` affiche un encart d’erreurs uniquement si la validation a échoué.  
Cela évite de polluer la page lorsqu’aucun problème n’est présent.

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/image-10.png)

formulaire avec erreurs

Bean mapping : du formulaire au modèle

Une fois validé, le formulaire est transformé en entité persistée :

```java
Booking booking = new Booking();
booking.adventurerName = form.adventurerName;
booking.arrivalDate = parsedDate;
booking.nights = form.nights;
booking.roomType = form.roomType;

booking.persist();

```

Le mapping est volontairement simple : on passe du formulaire à l’entité, puis à la persistance.  
Cela met en lumière le rôle de Qute : afficher les données, pas les transformer.

Le template de confirmation illustre aussi le formatage directement côté Qute :

```html
<p>
    Nous vous attendons le {booking.arrivalDate.format('dd/MM/yyyy')} pour un séjour de {booking.nights} nuits.
</p>

```

La date est formatée à l’affichage, sans logique métier supplémentaire.  
On garde le modèle propre et la présentation claire.

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/image-11.png)

réservation confirmé

### Registre et branche alternative

Le registre des réservations exploite la branche `#else` de Qute pour gérer le cas “vide” :

```html
{#for b in bookings}
<tr style="border-bottom: 1px dotted rgba(139, 69, 19, 0.4);">
    <td style="padding: 10px;">{b.adventurerName}</td>
    <td style="padding: 10px;">{b.arrivalDate.format('dd/MM/yyyy')}</td>
    <td style="padding: 10px;">{b.nights}</td>
    <td style="padding: 10px;">{b.roomType}</td>
</tr>
{#else}
<tr>
    <td colspan="4" style="text-align: center; padding: 2rem; font-style: italic;">Le registre est vide pour l'instant.</td>
</tr>
{/for}

```

Si aucune réservation n’est enregistrée, le tableau affiche un message clair.  
Sinon, chaque ligne montre le nom, la date formatée et la chambre choisie.

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/Capture-d---e--cran-2026-04-08-a---12.57.29.png)

![](https://storage.ghost.io/c/0b/31/0b31478e-a66a-46f1-8c34-d44388e21192/content/images/2026/04/Capture-d---e--cran-2026-04-08-a---12.57.48.png)

le registre des réservations

Mini lexique des directives Qute utilisées

Pour un moteur de templating, les “briques” sont les directives.  
Dans ce module, on rencontre surtout :

- `{#main ...}` et `{/main}` : insertion d’un template parent (tag).
- `{nested-content}` : zone où le contenu enfant est injecté.
- `{#if condition}` et `{/if}` : affichage conditionnel.
- `{#for item in items}` et `{/for}` : boucle sur une collection.
- `{#else}` : branche alternative d’une boucle ou d’une condition.
- `{variable}` : affichage d’une variable simple.
- `{object.field}` : accès à une propriété.

Exemples concrets vus dans la taverne : `{#for e in errors.values}`, `{#for drink in drinks}`, `{#if errors and !errors.isEmpty}`.

Ici, Qute n’est pas seul : il s’insère dans une chaîne cohérente **formulaire → validation → mapping → persistance → rendu**.

## Erreurs fréquentes en Qute

Même dans une taverne bien tenue, il arrive que des erreurs se glissent dans les parchemins.

- **Mauvais nom de variable** : `drinks` vs `drink` dans une boucle.  
Le template compile, mais le rendu échoue si la variable n’existe pas.
- **Oublier `{nested-content}` dans un tag** : le layout s’affiche, mais le contenu disparaît.
- **Type inattendu dans une page** : exemple, `booking` absent ou `null` dans un template de confirmation.
- **Branche vide non gérée** : oublier un `#else` dans un registre, et afficher un tableau vide sans explication.

Ces pièges sont évitables grâce à la validation à la compilation et à une structure de templates cohérente.

## Bonnes pratiques pour les templates Quarkus

Dans l’esprit d’une taverne organisée, quelques règles simples évitent bien des soucis.

- **Centraliser le layout** dans un tag (`main.html`) et garder les pages enfants minimalistes.
- **Limiter la logique** dans les templates : Qute affiche, Java prépare.
- **Préférer des modèles clairs** : passer des objets dédiés (ex : `BookingForm`) plutôt que des `Map` brutes.
- **Gérer les états vides** avec `#else` pour toujours afficher un message explicite.
- **Formater à l’affichage** (dates, montants) pour garder l’entité métier propre.

## Conclusion

Dans un donjon, on ne confie pas ses parchemins à un scribe maladroit.  
Qute est le scribe fiable de Quarkus : rapide, précis, et parfaitement intégré à Java.

Avec ses templates vérifiés à la compilation, son système de tags, et sa proximité avec le code métier,  
il permet de construire une interface web sans dénaturer l’architecture.

La taverne est prête.  
Les aventuriers aussi.  
Et chaque page s’affiche comme une promesse tenue.