🛢️Parcours Fioul GroupéGuide pas à pas — API Spring Boot
Accueil › Parcours 2 · API Spring Boot
🌱 Parcours 2 — API REST en couches

Construire l'API, couche par couche

On empile l'architecture dans l'ordre : entité → repository → DTO → service → contrôleur, puis validation et Swagger. À chaque couche : l'exemple médiathèque, puis à toi de l'écrire pour le fioul. L'API se pose sur la base MongoDB du parcours 1.

1
J'observe la coucheCode Spring commenté sur l'exemple « Livre ».
2
J'écris la mienneLa même couche pour « Campagne » et « Commande ».
Architecture cible — le flux d'une requête traverse les couches dans cet ordre :
HTTP → Controller → Service → Repository → MongoDB
                 ↕
             DTO (entrée/sortie)      Entity (@Document)
On code de l'intérieur vers l'extérieur : d'abord ce qui touche la base (entité, repository), puis la logique (service), enfin l'exposition (contrôleur).
0 Étape 0

Créer le projet Spring Boot

Générer un projet propre avec les bonnes dépendances, avant d'écrire la moindre classe.

Exemple start.spring.io

Sur Spring Initializr, projet Maven / Java 21, puis on ajoute les dépendances :

  • Spring Web — les contrôleurs REST
  • Spring Data MongoDB — l'accès à la base
  • Validation — @Valid, @NotNull…
  • Lombok (optionnel) — moins de code répétitif
# application.properties
spring.data.mongodb.uri=mongodb://localhost:27017/mediatheque
À toi — Fioul Le projet du back-end
  • Génère un projet fioul-api avec les mêmes dépendances + springdoc-openapi (étape 7).
  • Configure l'URI vers ta base fioul (locale, puis Atlas plus tard).
  • Ajoute le projet dans le dépôt fioul-groupe (dossier api/).
  • Vérifie que l'application démarre (mvn spring-boot:run).
Sécurité — ne mets jamais l'URI Atlas (avec mot de passe) en clair dans le dépôt. Utilise une variable d'environnement ou un secret GitHub.
1 Étape 1

L'entité — le reflet du document

Une classe annotée @Document représente une collection MongoDB. C'est le miroir Java du document modélisé au parcours 1.

Exemple LivreEntity
@Document(collection = "livres")
public class LivreEntity {
    @Id
    private String id;
    private String titre;
    private String isbn;
    private int anneePublication;
    private String auteurId;        // référence
    private List<Avis> avis;       // embarqué
    // getters / setters
}
À toi — Fioul CampagneEntity
  • Crée CampagneEntity annotée @Document("campagnes").
  • Champs : id, zone, statut (enum Statut), dateLimite, fournisseurId.
  • Ajoute List<Commande> commandes (classe embarquée avec litres, commune, adresse).
  • Crée FournisseurEntity avec sa liste de Palier.
Rappel — ta décision « embarquer / référencer » du parcours 1 se retrouve telle quelle ici : List<Commande> embarquée, fournisseurId référencé.
2 Étape 2

Le repository — l'accès aux données

Une interface qui étend MongoRepository fournit le CRUD gratuitement. On y ajoute des méthodes dérivées dont Spring devine l'implémentation d'après le nom.

Exemple LivreRepository
public interface LivreRepository
        extends MongoRepository<LivreEntity, String> {

    List<LivreEntity> findByAnneePublicationGreaterThan(int annee);
    Optional<LivreEntity> findByIsbn(String isbn);
}

findAll(), findById(), save(), deleteById() existent déjà, sans code.

À toi — Fioul CampagneRepository
  • Crée CampagneRepository extends MongoRepository<CampagneEntity, String>.
  • Ajoute findByStatut(Statut statut).
  • Ajoute findByZoneAndStatut(String zone, Statut statut).
  • Crée aussi FournisseurRepository.
Le lien avec le parcours 1 — findByZoneAndStatut produit exactement la requête que tu as écrite à la main en mongosh (étape 5).
3 Étape 3

Le DTO — ce que voit le client

On n'expose jamais l'entité directement. Un DTO (Data Transfer Object) définit précisément ce qui entre et ce qui sort de l'API : on masque certains champs, on en calcule d'autres.

Exemple LivreDTO (record)
public record LivreDTO(
    String id,
    String titre,
    String isbn,
    double noteMoyenne,   // calculé, pas stocké
    int nbAvis
) {}

Le record Java est parfait pour un DTO : immuable et concis.

À toi — Fioul CampagneDTO
  • Crée un record CampagneDTO exposé en lecture.
  • Champs stockés : id, zone, statut, dateLimite.
  • Champs calculés : volumeTotal et prixLitre (l'agrégation du parcours 1 !).
  • Crée un CreerCommandeDTO (entrée) : litres, commune, adresse.
Pourquoi séparer — le client n'a pas à connaître fournisseurId ni la liste brute des commandes ; il veut le volume et le prix. Le DTO fait ce tri.
4 Étape 4

Le service — la logique métier

Le service orchestre : il appelle le repository, applique les règles métier et transforme les entités en DTO. C'est le cerveau, entre le contrôleur et la base.

Exemple LivreService
@Service
public class LivreService {
    private final LivreRepository repo;
    // injection par constructeur
    public LivreService(LivreRepository r){ this.repo=r; }

    public List<LivreDTO> lister(){
        return repo.findAll().stream()
                   .map(this::versDTO).toList();
    }
    private LivreDTO versDTO(LivreEntity e){
        double moy = e.getAvis().stream()
              .mapToInt(Avis::getNote).average().orElse(0);
        return new LivreDTO(e.getId(), e.getTitre(),
              e.getIsbn(), moy, e.getAvis().size());
    }
}
À toi — Fioul CampagneService
  • Crée CampagneService avec injection par constructeur.
  • listerOuvertes(zone) → appelle le repository, mappe en DTO.
  • rejoindre(id, CreerCommandeDTO) → ajoute une commande (le $push de l'étape 4 MongoDB).
  • Écris calculerPrix(volume, fournisseur) qui choisit le bon palier.
  • Règle métier : refuser une inscription si la campagne est CLOTUREE ou la date dépassée.
5 Étape 5

Le contrôleur — la porte d'entrée REST

Le contrôleur relie les URLs HTTP aux méthodes du service. Il ne contient aucune logique métier : il reçoit, délègue, renvoie.

Exemple LivreController
@RestController
@RequestMapping("/api/livres")
public class LivreController {
    private final LivreService service;
    public LivreController(LivreService s){ this.service=s; }

    @GetMapping
    public List<LivreDTO> lister(){ return service.lister(); }

    @GetMapping("/{id}")
    public LivreDTO consulter(@PathVariable String id){
        return service.consulter(id);
    }
}
À toi — Fioul CampagneController

Implémente les endpoints du projet :

  • GET /api/campagnes (filtre ?zone=)
  • GET /api/campagnes/{id}
  • POST /api/campagnes (ouvrir)
  • POST /api/campagnes/{id}/commandes (rejoindre)
  • PUT /api/campagnes/{id}/cloturer
Teste tout de suite — avec Postman ou curl, vérifie chaque endpoint avant de passer à Flutter.
6 Étape 6

Valider les entrées

On ne fait jamais confiance aux données reçues. Les annotations de validation (@NotBlank, @Min…) + @Valid rejettent proprement les requêtes invalides.

Exemple Contraintes sur le DTO
public record CreerAvisDTO(
    @NotBlank String pseudo,
    @Min(0) @Max(5) int note,
    @Size(max=500) String commentaire
) {}

// dans le contrôleur :
public ... ajouter(@Valid @RequestBody CreerAvisDTO dto){ ... }
À toi — Fioul Sécuriser les inscriptions
  • @Min(500) sur litres (quantité minimale de commande).
  • @NotBlank sur commune et adresse.
  • Ajoute un @RestControllerAdvice qui renvoie un 400 lisible en cas d'erreur.
  • Règle métier (dans le service) : renvoyer 409 si la campagne est clôturée.
Passerelle Cyber — la validation d'entrée est ta première ligne de défense (injection, données aberrantes). À relier à ton cours de cybersécurité.
7 Étape 7

Documenter avec Swagger / OpenAPI

Une seule dépendance déploie une interface web qui documente et teste l'API. On enrichit avec quelques annotations.

Exemple springdoc-openapi
<!-- pom.xml -->
<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
  <version>2.6.0</version>
</dependency>

Sans configuration :

  • UI sur /swagger-ui/index.html
  • Description JSON sur /v3/api-docs
@Tag(name = "Livres")
@RestController
public class LivreController { ... }
À toi — Fioul Documenter l'API fioul
  • Ajoute la dépendance et lance Swagger UI.
  • Annote tes contrôleurs avec @Tag et @Operation(summary=...).
  • Teste POST /campagnes/{id}/commandes depuis « Try it out ».
  • Capture d'écran de Swagger → dans le README du projet.
Bilan — l'API est complète, validée, documentée et testable. C'est elle que l'appli Flutter va consommer au parcours 3.
← Parcours 1 · MongoDB