API REST complète de gestion de collecte de déchets, développée en Java natif avec Jakarta EE (sans Spring). Authentification Bearer, contrôle de rôles, SQL avancé, et 52 tests fonctionnels couvrant tous les endpoints — cas nominaux et cas d'erreur.
Contexte
SAÉ S4 — BUT Informatique, IUT de Lille. Projet évalué, durée environ 1 semestre.
Binôme : Victor Bredelle et Aubin Cambier. Partie A réalisée ensemble, Partie C réalisée seul par Aubin.
Concevoir et implémenter une API REST complète : modélisation BDD, endpoints CRUD, authentification et rôles, suite de tests fonctionnels.
Application fictive EcoDrop : gestion de points de collecte de déchets, dépôts et leaderboard de contributeurs.
Équipe & contribution personnelle
Technologies
Architecture
Pas de Spring : chaque route est dispatchée manuellement via pathInfo(). Le PATCH est géré en surchargeant service() car HttpServlet ne le supporte pas nativement.
Fonctionnalités
| Méthode | Route | Description | Accès |
|---|---|---|---|
| GET | /auth/token | HTTP Basic Auth → token UUID Bearer | Public |
| GET | /waste | Liste des types de déchets (JSON ou XML selon Accept) | Public |
| POST | /deposits | Créer un dépôt — rejet 409 si point saturé (capacity check) | User |
| PATCH | /points/{id}/clear | Marque tous les dépôts du point comme collectés (sans suppression) | Admin |
| GET | /points/overloaded | Points dont l'occupation dépasse 80% — SQL HAVING | Admin |
| GET | /leaderboard | Top 10 utilisateurs par score — SQL GROUP BY + LEFT JOIN + COALESCE | Public |
| DELETE | /waste/{id} | Suppression d'un type de déchet | Admin |
| PUT | /users/{id} | Mise à jour complète d'un utilisateur | Admin |
Base de données
Compétences techniques
Sans Spring, chaque route est dispatchée en lisant pathInfo() et en branchant sur des if/switch. Le PATCH n'étant pas supporté par HttpServlet, j'ai appris à surcharger service() pour l'intercepter avant le dispatch standard.
Le TokenAuthFilter (@WebFilter) génère un UUID stocké en HttpSession Tomcat. J'ai participé à l'implémentation des vérifications de rôle (user vs admin) en m'assurant que chaque cas (GET public, DELETE admin only, POST user only) était couvert et testé.
Le leaderboard utilise un LEFT JOIN + COALESCE + GROUP BY pour inclure les utilisateurs sans dépôt (score = 0). La détection de surcharge repose sur un HAVING SUM(kilos) > capaciteMax * 0.8. La vérification de capacité avant insertion utilise une sous-requête d'agrégation.
Chaque domaine (waste, collectionpoint, deposit, users) dispose de son propre DAO qui encapsule toutes les requêtes JDBC. Les DTOs assurent que la couche Controller ne manipule jamais directement les objets SQL — cette isolation a facilité le débogage et les tests.
J'ai écrit et corrigé 52 fichiers de tests Bruno couvrant tous les endpoints : 200, 201, 204 pour les cas nominaux, et 400, 401, 403, 404, 409 pour les cas d'erreur. Cette couverture a permis de détecter des bugs subtils (id incorrect en réponse 404, PUT se comportant comme un PATCH).
Travail sur branches séparées avec Victor. J'ai géré les merges entre la branche de conception commune (Partie A) et ma branche d'implémentation (Partie C), en veillant à ne pas écraser les choix d'architecture initiaux tout en ajoutant mes propres couches.
Compétences transversales
Quand les tests Bruno retournaient un code HTTP 404 avec l'id de la donnée au lieu de l'id du paramètre, j'ai isolé le bug, remonté au controller concerné et proposé la correction sans attendre l'encadrement. J'ai appris à lire des stacktraces Tomcat et à croiser les logs avec le comportement attendu.
J'ai étendu des fonctionnalités initialement développées par le lead technique (Victor) sans documentation : j'ai lu le code existant pour comprendre les conventions adoptées (nommage des DAO, structure des DTOs) avant d'ajouter les endpoints PUT/PATCH users et deposits en cohérence avec l'architecture en place.
Le travail était découpé par domaine métier, ce qui a permis à chaque membre d'avancer en parallèle sans bloquer les autres. Cette organisation m'a appris à définir des interfaces claires avec l'équipe avant de commencer à coder, pour éviter de devoir refactorer les DAO après coup.
Lorsque j'ai détecté des incohérences entre le comportement d'un endpoint et la spécification, j'ai documenté le bug (input, réponse reçue, réponse attendue) et l'ai communiqué clairement à l'équipe, ce qui a accéléré la correction en évitant les allers-retours.
Analyse réflexive
Coder une API sans Spring oblige à comprendre HTTP réellement : les codes de statut, les headers Accept/Content-Type, la gestion des méthodes non standard comme PATCH. Je ne me contente plus d'utiliser un framework comme une boîte noire — je sais ce qu'il y a dessous.
Écrire les 52 tests Bruno m'a obligé à lire la spécification attentivement et à anticiper tous les cas limites (409, 403, 401, capacité exactement à 80%…). Les tests ne sont pas une formalité : ils m'ont aidé à trouver des bugs que la revue de code n'avait pas détectés.
Sans Spring, le routage sur pathInfo() devient vite un enchevêtrement de conditions. Une faute de frappe dans un chemin, un else if mal placé, et la requête tombe dans le mauvais handler. J'ai compris pourquoi les frameworks existent : ils résolvent des problèmes réels, pas du confort.
Le TokenAuthFilter a demandé plusieurs itérations : GET public vs GET protégé (ex : /points/overloaded admin only), combinaisons user/admin sur le même endpoint selon la méthode HTTP. Chaque cas manqué était un test Bruno qui échouait avec un 403 inattendu.
J'utiliserais Spring Boot pour éviter le boilerplate du routing manuel et de la gestion des sessions. Surtout, j'écrirais les tests Bruno en parallèle du développement (TDD) plutôt qu'à la fin : les bugs auraient été détectés plus tôt, au moment où le contexte du développeur est encore frais.
La version finale du schéma manque de contraintes CHECK (ex : kilos > 0, role IN ('user', 'admin')). Ces contraintes auraient dû être définies dès le départ dans init.sql pour déléguer une partie de la validation à la base de données et alléger les contrôles dans les DAO.