OpenAPI et Swagger UI : documenter son API Spring Boot

Publié le · Mis à jour le

L'idée en une phrase

OpenAPI est un format standard pour décrire une API REST : ses adresses, ce qu'elle attend, ce qu'elle renvoie, ses erreurs. Dans un projet Spring Boot, ce document peut être généré automatiquement depuis ton code et affiché dans une page web, Swagger UI, où l'on peut tester l'API directement.

Une image pour comprendre : la carte du restaurant

Dans un restaurant, le client ne va pas en cuisine pour savoir ce qu'il peut commander. Il lit la carte : le nom des plats, ce qu'ils contiennent, leur prix. La carte est un contrat entre la cuisine et la salle.

OpenAPI, c'est la carte de ton API :

  • chaque plat est une adresse de l'API (GET /produits/{id}) ;
  • la description dit ce qu'il faut fournir et ce qu'on reçoit ;
  • la carte est écrite dans un format standard, que tout le monde sait lire : le développeur front-end, une autre équipe, un outil qui génère du code.

Et comme une carte, elle n'a de valeur que si elle est à jour : un plat retiré de la cuisine mais encore sur la carte, c'est un client mécontent.

Le vocabulaire : OpenAPI, Swagger, springdoc

Ces trois noms reviennent ensemble et sont souvent confondus :

Nom Ce que c'est
OpenAPI le format de description, un standard ouvert (version 3.x)
Swagger l'ancien nom du format (jusqu'à la version 2.0) ; aujourd'hui, le nom d'une famille d'outils
Swagger UI une page web qui affiche un document OpenAPI de façon lisible, et permet de tester l'API
springdoc-openapi une bibliothèque qui génère le document OpenAPI à partir de ton code Spring Boot

Quand quelqu'un te dit « regarde le Swagger », il parle presque toujours de la page Swagger UI.

À quoi ressemble un document OpenAPI ?

C'est un fichier YAML ou JSON. Tu n'as pas besoin de l'écrire à la main, mais il faut savoir le lire :

openapi: 3.1.0
info:
  title: API Boutique
  version: 1.0.0
paths:
  /produits/{id}:
    get:
      summary: Afficher un produit
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        "200":
          description: Le produit
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProduitDto"
        "404":
          description: Produit introuvable
components:
  schemas:
    ProduitDto:
      type: object
      required: [nom, prix]
      properties:
        nom:
          type: string
          example: Clavier mécanique
        prix:
          type: number
          example: 89.90

À lire comme ceci : « GET /produits/{id} attend un identifiant entier dans l'adresse. En cas de succès (200), elle renvoie un ProduitDto avec un nom et un prix, tous deux obligatoires. Si le produit n'existe pas, elle renvoie une 404. »

Le bloc components contient les schémas réutilisables, c'est-à-dire la forme des objets JSON. $ref y fait référence.

Générer la documentation avec Spring Boot

Une dépendance

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>...</version>
</dependency>

⚠️ La version de springdoc doit correspondre à celle de Spring Boot :

Spring Boot springdoc-openapi
4.x 3.x
3.x 2.x
2.x 1.x (ancien nom : springdoc-openapi-ui)

Une version qui ne correspond pas donne une erreur au démarrage, ou une page Swagger UI introuvable.

Deux adresses

Lance l'application, puis ouvre :

Adresse Contenu
http://localhost:8080/v3/api-docs le document OpenAPI, en JSON
http://localhost:8080/swagger-ui/index.html la page Swagger UI

Ce qui est généré sans rien écrire

springdoc lit ton code au démarrage et en déduit beaucoup de choses :

@RestController
@RequestMapping("/produits")
public class ProduitController {

    @GetMapping("/{id}")
    public ProduitDto afficher(@PathVariable Long id) { ... }

    @PostMapping
    public ResponseEntity<ProduitDto> creer(@Valid @RequestBody CreationProduitDto dto) { ... }
}
public record CreationProduitDto(
        @NotBlank @Size(max = 100) String nom,
        @NotNull @Positive BigDecimal prix) {}
Dans ton code Dans la documentation
@GetMapping("/{id}") l'adresse et la méthode HTTP
@PathVariable Long id un paramètre obligatoire, de type entier
le record CreationProduitDto le schéma du corps de la requête
@NotBlank, @NotNull le champ est marqué obligatoire
@Size(max = 100) la longueur maximale

C'est un vrai avantage : la documentation est construite à partir du code, donc elle suit les modifications. Et les règles de validation que tu écris pour Spring apparaissent aussi dans la documentation.

Améliorer la documentation avec des annotations

Le code ne dit pas tout : à quoi sert l'adresse, quelles erreurs elle peut renvoyer, un exemple de valeur. Quelques annotations complètent la carte :

@RestController
@RequestMapping("/produits")
@Tag(name = "Produits", description = "Consultation et gestion du catalogue")
public class ProduitController {

    @Operation(summary = "Afficher un produit")
    @ApiResponse(responseCode = "200", description = "Le produit")
    @ApiResponse(responseCode = "404", description = "Aucun produit avec cet identifiant")
    @GetMapping("/{id}")
    public ProduitDto afficher(@PathVariable Long id) { ... }
}
public record ProduitDto(
        @Schema(description = "Nom affiché dans le catalogue", example = "Clavier mécanique")
        String nom,
        @Schema(description = "Prix TTC en euros", example = "89.90")
        BigDecimal prix) {}
Annotation Rôle
@Tag regroupe les adresses par thème dans Swagger UI
@Operation décrit en une phrase ce que fait l'adresse
@ApiResponse documente un code de réponse possible, erreurs comprises
@Schema décrit un champ et donne un exemple

Astuce : n'annote pas tout par principe. Un @Operation(summary = "Créer un produit") sur une méthode creer n'apprend rien. Concentre-toi sur ce que le code ne dit pas : les erreurs possibles, l'unité d'un montant, le format d'une date, un exemple réaliste.

Documenter les erreurs

C'est la partie la plus souvent oubliée, et la plus utile pour ceux qui appellent ton API. Si ton @RestControllerAdvice renvoie une 404 ou une 409 (voir la fiche sur les exceptions), la documentation doit le dire. Sinon, le développeur front-end découvre ces erreurs en production.

Tester avec Swagger UI

Dans Swagger UI, chaque adresse a un bouton Try it out. Tu remplis les paramètres et le corps de la requête, tu cliques sur Execute, et la page affiche :

  • la requête réellement envoyée (avec la commande curl équivalente) ;
  • la réponse : code HTTP, en-têtes, corps JSON.

C'est très pratique pour vérifier rapidement une adresse que tu viens d'écrire, sans outil supplémentaire.

Note : Swagger UI envoie de vraies requêtes à l'application. Sur ton poste, aucun risque. Sur un environnement partagé, une requête DELETE supprime vraiment des données.

Deux façons de travailler

Approche Le principe Quand ?
Code first (le code d'abord) on écrit les contrôleurs, la documentation est générée le cas le plus courant, celui de cette fiche
Contract first (le contrat d'abord) on écrit d'abord le document OpenAPI, puis on génère le code quand plusieurs équipes doivent se mettre d'accord avant de développer

Dans l'approche contract first, un outil comme openapi-generator lit le fichier YAML et génère les interfaces des contrôleurs, les DTO, ou un client prêt à l'emploi pour appeler l'API.

C'est aussi utile dans l'autre sens : si une autre équipe te fournit son document OpenAPI, tu peux générer le client Java pour appeler son API, au lieu d'écrire les DTO à la main. Si ton projet utilise ce générateur, ne modifie jamais le code généré : il serait écrasé à la prochaine génération.

La documentation est un contrat

Les applications qui appellent ton API (un front-end, une application mobile, une autre équipe) s'appuient sur ce contrat. Certaines modifications le cassent :

Modification Effet pour ceux qui appellent l'API
ajouter un champ dans une réponse ✅ sans danger en général
ajouter une nouvelle adresse ✅ sans danger
renommer un champ (prix → montant) ❌ leur code ne trouve plus le champ
supprimer un champ ou une adresse ❌ leur code plante
rendre obligatoire un champ qui ne l'était pas ❌ leurs requêtes sont refusées
changer le type d'un champ (nombre → texte) ❌ leur lecture du JSON échoue

Ce sont des modifications qui marchent parfaitement dans ton application et tes tests, mais qui cassent celle des autres.

Avant de renommer ou de supprimer quelque chose dans un DTO de réponse, demande qui utilise l'API.

Et en production ?

La page Swagger UI expose la liste complète de tes adresses et la forme de tes données. C'est précieux pour ceux qui développent, mais aussi pour quelqu'un qui chercherait une faille.

Dans beaucoup de projets, elle est désactivée en production, avec un profil Spring dédié :

# application-prod.properties
springdoc.api-docs.enabled=false
springdoc.swagger-ui.enabled=false

D'autres projets la gardent, mais protégée par une authentification. Le choix appartient à l'équipe ; l'important est que ce soit un choix, pas un oubli.

Les bonnes habitudes

  1. Garde la version de springdoc alignée sur celle de Spring Boot.
  2. Documente les erreurs que chaque adresse peut renvoyer, pas seulement le cas qui marche.
  3. Donne des exemples réalistes avec @Schema(example = ...) : un exemple vaut souvent mieux qu'une longue description.
  4. Annote ce que le code ne dit pas, pas ce qu'il dit déjà.
  5. Utilise la validation (@NotBlank, @Size…) : elle protège ton API et apparaît dans la documentation.
  6. Ne renomme pas un champ de réponse sans prévenir : c'est une rupture du contrat.
  7. Désactive ou protège Swagger UI en production.
  8. Ne modifie jamais du code généré par openapi-generator.

À retenir

  • OpenAPI est le format standard qui décrit une API REST ; Swagger UI est la page web qui l'affiche et permet de la tester.
  • Avec springdoc-openapi, la documentation est générée depuis ton code, validation comprise.
  • Quelques annotations (@Tag, @Operation, @ApiResponse, @Schema) complètent ce que le code ne dit pas, en particulier les erreurs.
  • La documentation est un contrat : renommer ou supprimer un champ casse les applications qui appellent ton API.
  • En production, Swagger UI est désactivée ou protégée.

Avis sur l'article

Pas encore de note : soyez le premier à donner votre avis.

Les avis sont publiés après validation par le modérateur.

Votre note