Création de votre première API RESTful avec Spring Boot
Ce cours s'inscrit dans le cadre de votre formation en "Développement Backend Robuste avec Java et Spring Boot". Nous allons ensemble franchir une étape cruciale : la conception et l'implémentation de votre première API RESTful. Les API (Application Programming Interfaces) sont le cœur de la communication entre les différentes parties d'une application distribuée, qu'il s'agisse de frontends web, d'applications mobiles, ou d'autres services backend.
Introduction
Dans le monde moderne du développement logiciel, les applications ne sont plus de simples monolithes. Elles sont souvent composées de multiples services qui communiquent entre eux, et avec des clients variés. C'est là que les API RESTful entrent en jeu.
-
Qu'est-ce qu'une API ? Une API est un ensemble de définitions et de protocoles qui permet à différentes applications de communiquer entre elles. Elle définit comment les développeurs peuvent interagir avec un service pour demander des informations ou exécuter des actions.
-
Qu'est-ce que REST ? REST (Representational State Transfer) est un style architectural pour les systèmes hypermédia distribués. Il repose sur un ensemble de principes :
- Client-Serveur : Séparation des préoccupations entre l'interface utilisateur (client) et le stockage des données (serveur).
- Sans état (Stateless) : Chaque requête du client vers le serveur doit contenir toutes les informations nécessaires à la compréhension de la requête. Le serveur ne doit pas stocker de contexte client entre les requêtes.
- Mise en cache (Cacheable) : Les réponses doivent être définies comme étant mises en cache ou non, pour améliorer les performances.
- Système en couches (Layered System) : Un client ne peut généralement pas savoir s'il est directement connecté au serveur final ou à un intermédiaire.
- Code à la demande (Code on Demand) : (Optionnel) Les serveurs peuvent étendre les fonctionnalités des clients en téléchargeant et exécutant du code.
- Interface Uniforme : C'est le principe le plus important, qui inclut :
- Identification des ressources par URI.
- Manipulation des ressources par des représentations.
- Messages auto-descriptifs.
- Hypermedia as the Engine of Application State (HATEOAS).
-
Pourquoi Spring Boot pour les API RESTful ? Spring Boot est devenu le framework de choix pour le développement d'applications Java robustes et de microservices. Il offre de nombreux avantages pour les API REST :
- Démarrage rapide : Moins de configuration, plus de code.
- Auto-configuration : Détecte les dépendances et configure automatiquement le projet.
- Serveur embarqué : Tomcat, Jetty ou Undertow intégrés, permettant de lancer l'application directement avec un
mainméthode. - Écosystème Spring : Accès à toutes les fonctionnalités avancées de Spring (sécurité, transactions, ORM, etc.).
Dans cette leçon, nous allons construire une API RESTful simple pour gérer une liste de tâches (une "To-Do List"). Nous verrons comment créer, lire, mettre à jour et supprimer des tâches.
Prérequis
Avant de commencer, assurez-vous d'avoir les éléments suivants installés et configurés sur votre machine :
- Java Development Kit (JDK) 17 ou plus récent : Vérifiez avec
java -versiondans votre terminal. - Maven ou Gradle : Maven est préconisé pour ce tutoriel. Vérifiez avec
mvn -v. - Un IDE (Environnement de Développement Intégré) :
- IntelliJ IDEA Community Edition (recommandé)
- VS Code avec l'extension Java
- Eclipse IDE for Enterprise Java and Web Developers
- Des connaissances de base en Java (classes, objets, interfaces, collections).
- Une compréhension rudimentaire des méthodes HTTP (GET, POST, PUT, DELETE).
Étape 1 : Initialisation du projet Spring Boot
La manière la plus simple et rapide de créer un projet Spring Boot est d'utiliser Spring Initializr.
-
Ouvrez votre navigateur et accédez à https://start.spring.io/.
-
Configurez votre projet comme suit :
- Project : Maven Project
- Language : Java
- Spring Boot : Choisissez la dernière version stable (par exemple, 3.x.x).
- Project Metadata :
- Group :
com.example(ou votre propre package) - Artifact :
todo-api(nom de votre projet) - Name :
todo-api - Description :
Demo project for Spring Boot REST API - Package Name :
com.example.todoapi - Packaging : Jar
- Java : 17 (ou la version de votre JDK)
- Group :
- Dependencies : Ajoutez les dépendances suivantes. Tapez simplement leur nom dans le champ "Add Dependencies" :
- Spring Web : Pour construire des applications web, y compris des API RESTful.
- Spring Data JPA : Pour l'accès aux données avec Java Persistence API (JPA).
- H2 Database : Une base de données en mémoire, parfaite pour le développement et les tests.
- Lombok : Une bibliothèque qui réduit le code répétitif (boilerplate) via des annotations. C'est une dépendance facultative mais très utile.
-
Cliquez sur le bouton "Generate" pour télécharger l'archive
todo-api.zip. -
Décompressez le fichier ZIP dans un répertoire de votre choix.
-
Ouvrez votre IDE et importez le projet Maven (File -> Open -> sélectionnez le dossier décompressé). Laissez l'IDE télécharger toutes les dépendances.
Votre projet est maintenant initialisé et prêt à être codé !
Étape 2 : Comprendre l'Architecture RESTful
Avant de plonger dans le code, rappelons les concepts clés de l'architecture RESTful que nous allons appliquer :
- Ressources : Dans notre API de liste de tâches, la ressource principale est la Tâche (
Task). Chaque tâche est une entité unique que nous allons manipuler. - URI (Uniform Resource Identifier) : Chaque ressource doit avoir une adresse unique. Pour nos tâches, les URIs seront typiquement :
/tasks: Représente la collection de toutes les tâches./tasks/{id}: Représente une tâche spécifique, identifiée par son{id}.
- Méthodes HTTP : Elles définissent les actions que vous pouvez effectuer sur les ressources.
GET: Récupérer une ressource ou une collection de ressources. (Ex:GET /taskspour toutes les tâches,GET /tasks/1pour la tâche avec l'ID 1).POST: Créer une nouvelle ressource. Le corps de la requête contient les données de la nouvelle ressource. (Ex:POST /taskspour créer une nouvelle tâche).PUT: Mettre à jour complètement une ressource existante. Le corps de la requête contient l'état complet de la ressource mise à jour. (Ex:PUT /tasks/1pour modifier la tâche avec l'ID 1).DELETE: Supprimer une ressource. (Ex:DELETE /tasks/1pour supprimer la tâche avec l'ID 1).
- Statuts HTTP : Les codes de statut HTTP indiquent le résultat de l'opération demandée.
200 OK: La requête a réussi.201 Created: La ressource a été créée avec succès (souvent en réponse à un POST).204 No Content: La requête a réussi mais il n'y a pas de contenu à retourner (souvent en réponse à un DELETE réussi).400 Bad Request: La requête est mal formée.404 Not Found: La ressource demandée n'existe pas.500 Internal Server Error: Une erreur inattendue s'est produite côté serveur.
Étape 3 : Création des Composants de l'API
Notre API sera composée de trois couches principales, suivant les principes de Spring :
- Modèle (Model/Entity) : Représente la structure de notre donnée
Tasket sa persistance. - Repository : Interface pour l'accès aux données (CRUD).
- Contrôleur (Controller) : Reçoit les requêtes HTTP, interagit avec le repository et renvoie les réponses.
3.1. Le Modèle (Entity) : Task.java
Créez un nouveau package com.example.todoapi.model (ou entity) et à l'intérieur, un fichier Task.java. Cette classe représentera notre ressource "Tâche".
package com.example.todoapi.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity // Indique que cette classe est une entité JPA et sera mappée à une table de base de données
@Data // Annotation Lombok pour générer getters, setters, toString, equals et hashCode
@NoArgsConstructor // Annotation Lombok pour générer un constructeur sans arguments
@AllArgsConstructor // Annotation Lombok pour générer un constructeur avec tous les arguments
public class Task {
@Id // Marque le champ 'id' comme la clé primaire
@GeneratedValue(strategy = GenerationType.IDENTITY) // Spécifie que la valeur de l'ID sera générée automatiquement par la base de données
private Long id;
private String description;
private boolean completed; // Statut de la tâche (achevée ou non)
}
@Entity: Annotation JPA essentielle qui indique à Hibernate (l'implémentation par défaut de JPA dans Spring Boot) que cette classe est une entité persistante et qu'une tabletask(par défaut) doit être créée dans la base de données pour la stocker.@Id: Marque le champidcomme la clé primaire de l'entité.@GeneratedValue(strategy = GenerationType.IDENTITY): Configure la génération automatique de la valeur de la clé primaire.IDENTITYsignifie que la base de données gérera elle-même l'auto-incrémentation.- Lombok (
@Data,@NoArgsConstructor,@AllArgsConstructor) : Ces annotations réduisent considérablement la quantité de code "boilerplate" (répétitif).@Datagénère automatiquement les getters, setters,equals(),hashCode()ettoString().@NoArgsConstructoret@AllArgsConstructorgénèrent les constructeurs correspondants.
3.2. Le Repository : TaskRepository.java
Créez un nouveau package com.example.todoapi.repository et un fichier TaskRepository.java.
package com.example.todoapi.repository;
import com.example.todoapi.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository // Indique que cette interface est un composant de persistance Spring
public interface TaskRepository extends JpaRepository<Task, Long> {
// JpaRepository fournit déjà toutes les opérations CRUD de base (save, findById, findAll, deleteById, etc.)
// Vous pouvez ajouter ici des méthodes de requête personnalisées si nécessaire,
// par exemple: List<Task> findByCompleted(boolean completed);
}
@Repository: Annotation Spring qui indique que cette interface est un composant de la couche de persistance. Spring détectera et gérera cette interface comme un bean.extends JpaRepository<Task, Long>: C'est le cœur du Spring Data JPA. En étendantJpaRepository, notre interfaceTaskRepositoryhérite automatiquement d'une multitude de méthodes pour effectuer des opérations CRUD (Create, Read, Update, Delete) sur notre entitéTask.- Le premier paramètre
<Task>spécifie le type de l'entité que ce repository gère. - Le second paramètre
<Long>spécifie le type de la clé primaire de l'entitéTask.
- Le premier paramètre
3.3. Le Contrôleur : TaskController.java
Créez un nouveau package com.example.todoapi.controller et un fichier TaskController.java. Ce sera la couche d'interaction avec le client.
package com.example.todoapi.controller;
import com.example.todoapi.model.Task;
import com.example.todoapi.repository.TaskRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
@RestController // Indique que cette classe est un contrôleur REST, qui gère les requêtes HTTP et retourne des données JSON/XML
@RequestMapping("/api/tasks") // Définit le chemin de base pour toutes les requêtes de ce contrôleur
public class TaskController {
@Autowired // Injecte une instance de TaskRepository, gérée par Spring
private TaskRepository taskRepository;
// GET /api/tasks : Récupérer toutes les tâches
@GetMapping // Mappe cette méthode aux requêtes GET sur /api/tasks
public List<Task> getAllTasks() {
return taskRepository.findAll(); // Retourne toutes les tâches de la base de données
}
// GET /api/tasks/{id} : Récupérer une tâche par son ID
@GetMapping("/{id}") // Mappe cette méthode aux requêtes GET sur /api/tasks/{id}
public ResponseEntity<Task> getTaskById(@PathVariable Long id) { // @PathVariable extrait l'ID de l'URI
Optional<Task> task = taskRepository.findById(id); // Cherche la tâche par ID
return task.map(ResponseEntity::ok) // Si la tâche est trouvée, retourne 200 OK avec la tâche
.orElseGet(() -> ResponseEntity.notFound().build()); // Sinon, retourne 404 Not Found
}
// POST /api/tasks : Créer une nouvelle tâche
@PostMapping // Mappe cette méthode aux requêtes POST sur /api/tasks
@ResponseStatus(HttpStatus.CREATED) // Définit le code de statut HTTP à 201 CREATED en cas de succès
public Task createTask(@RequestBody Task task) { // @RequestBody mappe le corps de la requête JSON/XML à un objet Task
return taskRepository.save(task); // Enregistre la nouvelle tâche dans la base de données
}
// PUT /api/tasks/{id} : Mettre à jour une tâche existante
@PutMapping("/{id}") // Mappe cette méthode aux requêtes PUT sur /api/tasks/{id}
public ResponseEntity<Task> updateTask(@PathVariable Long id, @RequestBody Task taskDetails) {
Optional<Task> task = taskRepository.findById(id); // Cherche la tâche existante
if (task.isPresent()) {
Task existingTask = task.get();
existingTask.setDescription(taskDetails.getDescription()); // Met à jour la description
existingTask.setCompleted(taskDetails.isCompleted()); // Met à jour le statut complété
return ResponseEntity.ok(taskRepository.save(existingTask)); // Sauvegarde et retourne la tâche mise à jour avec 200 OK
} else {
return ResponseEntity.notFound().build(); // Si la tâche n'existe pas, retourne 404 Not Found
}
}
// DELETE /api/tasks/{id} : Supprimer une tâche
@DeleteMapping("/{id}") // Mappe cette méthode aux requêtes DELETE sur /api/tasks/{id}
@ResponseStatus(HttpStatus.NO_CONTENT) // Définit le code de statut HTTP à 204 NO CONTENT en cas de succès
public void deleteTask(@PathVariable Long id) {
taskRepository.deleteById(id); // Supprime la tâche par ID
}
}
@RestController: C'est une annotation composite de@Controlleret@ResponseBody. Elle indique que cette classe est un contrôleur Spring MVC et que toutes les méthodes retournent directement des objets qui doivent être sérialisés en JSON ou XML dans la réponse HTTP.@RequestMapping("/api/tasks"): Définit le chemin de base (ou URI racine) pour toutes les requêtes gérées par ce contrôleur. Ainsi, toutes les routes définies dans ce contrôleur commenceront par/api/tasks.@Autowired: C'est une annotation pour l'injection de dépendances. Spring va automatiquement trouver une instance deTaskRepositoryet l'injecter dans cette variable. C'est ce qu'on appelle l'Inversion de Contrôle (IoC).@GetMapping,@PostMapping,@PutMapping,@DeleteMapping: Ce sont des annotations de mapping qui lient les méthodes du contrôleur à des types de requêtes HTTP spécifiques et des chemins d'URI.@PathVariable Long id: Utilisée pour extraire une valeur de chemin (comme{id}dans l'URI) et la lier à un paramètre de méthode.@RequestBody Task task: Indique que les données du corps de la requête HTTP (généralement JSON ou XML) doivent être converties en un objetTask. Spring Boot utilise par défaut Jackson pour la conversion JSON.ResponseEntity<T>: Une classe très utile dans Spring MVC pour représenter la réponse HTTP complète. Elle vous permet de contrôler non seulement le corps de la réponse, mais aussi les en-têtes HTTP et le code de statut HTTP.@ResponseStatus(HttpStatus.CREATED)et@ResponseStatus(HttpStatus.NO_CONTENT): Ces annotations définissent le code de statut HTTP de la réponse en cas de succès de la méthode.HttpStatus.CREATED(201) est standard pour les opérations de création réussies, etHttpStatus.NO_CONTENT(204) pour les suppressions réussies où aucune information n'est renvoyée.
Étape 4 : Configuration de la Base de Données H2
Pour que notre application puisse utiliser la base de données H2, nous devons ajouter quelques lignes de configuration dans le fichier src/main/resources/application.properties.
Ouvrez application.properties et ajoutez ce qui suit :
# Configuration de la base de données H2
spring.datasource.url=jdbc:h2:mem:todo_db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Configuration Hibernate/JPA
spring.jpa.hibernate.ddl-auto=update # Crée ou met à jour le schéma de la base de données automatiquement au démarrage
spring.jpa.show-sql=true # Affiche les requêtes SQL générées dans la console
# Activer la console H2 pour visualiser la base de données
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.url=jdbc:h2:mem:todo_db: Spécifie que nous utilisons une base de données H2 en mémoire nomméetodo_db. Les données seront perdues à chaque redémarrage de l'application.spring.jpa.hibernate.ddl-auto=update: C'est une propriété très pratique pour le développement.updatesignifie qu'Hibernate tentera de créer ou de mettre à jour le schéma de la base de données en fonction de vos entités JPA. D'autres options incluentcreate(recrée la DB à chaque fois),create-drop(recrée puis supprime à l'arrêt), etnone(ne fait rien, utile en production).spring.h2.console.enabled=trueetspring.h2.console.path=/h2-console: Ces lignes activent la console web H2, un outil très pratique pour visualiser et interroger votre base de données en mémoire via un navigateur.
Étape 5 : Exécution et Test de l'API
Maintenant que tous les composants sont en place, nous pouvons lancer notre application et tester les endpoints.
-
Exécuter l'application :
- Dans votre IDE, naviguez vers la classe principale de votre application (généralement
TodoApiApplication.javadans le package racinecom.example.todoapi). - Exécutez la méthode
mainde cette classe (clic droit -> RunTodoApiApplication.main()). - Vous devriez voir des logs indiquant que Spring Boot démarre sur le port 8080 (par défaut).
- Dans votre IDE, naviguez vers la classe principale de votre application (généralement
-
Accéder à la console H2 :
- Ouvrez votre navigateur et allez à
http://localhost:8080/h2-console. - Assurez-vous que l'URL JDBC est
jdbc:h2:mem:todo_db(ou celle configurée dansapplication.properties), le nom d'utilisateursaet le mot de passepassword. - Cliquez sur "Connect". Vous devriez voir l'explorateur de base de données et votre table
TASK(outask, selon votre configuration H2).
- Ouvrez votre navigateur et allez à
-
Tester l'API avec Postman, Insomnia ou cURL :
-
Créer une tâche (POST)
- Méthode :
POST - URL :
http://localhost:8080/api/tasks - Headers :
Content-Type: application/json - Body (raw JSON) :
{ "description": "Apprendre Spring Boot", "completed": false } - Résultat attendu : Statut
201 Createdet l'objet tâche créé avec son ID.{ "id": 1, "description": "Apprendre Spring Boot", "completed": false } - Exemple cURL :
curl -X POST -H "Content-Type: application/json" -d '{"description": "Apprendre Spring Boot", "completed": false}' http://localhost:8080/api/tasks
- Méthode :
-
Créer une autre tâche (POST)
- Body (raw JSON) :
{ "description": "Préparer le cours Java", "completed": true } - Résultat attendu : Statut
201 Createdet l'objet tâche créé avec un nouvel ID (par exemple, 2). - Exemple cURL :
curl -X POST -H "Content-Type: application/json" -d '{"description": "Préparer le cours Java", "completed": true}' http://localhost:8080/api/tasks
- Body (raw JSON) :
-
Récupérer toutes les tâches (GET)
- Méthode :
GET - URL :
http://localhost:8080/api/tasks - Résultat attendu : Statut
200 OKet une liste de toutes les tâches. - Exemple cURL :
curl http://localhost:8080/api/tasks
- Méthode :
-
Récupérer une tâche par ID (GET)
- Méthode :
GET - URL :
http://localhost:8080/api/tasks/1(remplacez 1 par l'ID réel) - Résultat attendu : Statut
200 OKet l'objet de la tâche correspondante, ou404 Not Foundsi l'ID n'existe pas. - Exemple cURL :
curl http://localhost:8080/api/tasks/1
- Méthode :
-
Mettre à jour une tâche (PUT)
- Méthode :
PUT - URL :
http://localhost:8080/api/tasks/1(remplacez 1 par l'ID de la tâche à modifier) - Headers :
Content-Type: application/json - Body (raw JSON) :
{ "id": 1, "description": "Apprendre Spring Boot en profondeur", "completed": true } - Résultat attendu : Statut
200 OKet l'objet tâche mis à jour. - Exemple cURL :
curl -X PUT -H "Content-Type: application/json" -d '{"id": 1, "description": "Apprendre Spring Boot en profondeur", "completed": true}' http://localhost:8080/api/tasks/1
- Méthode :
-
Supprimer une tâche (DELETE)
- Méthode :
DELETE - URL :
http://localhost:8080/api/tasks/1(remplacez 1 par l'ID de la tâche à supprimer) - Résultat attendu : Statut
204 No Content. - Exemple cURL :
curl -X DELETE http://localhost:8080/api/tasks/1
- Méthode :
-
Félicitations ! Vous avez créé, lancé et testé votre première API RESTful complète avec Spring Boot.
Concepts Avancés et Bonnes Pratiques
Bien que notre API soit fonctionnelle, une application de production nécessiterait des considérations supplémentaires :
- Service Layer : Pour une meilleure séparation des préoccupations, il est recommandé d'introduire une couche de service (
TaskService). Le contrôleur appellerait le service, qui à son tour interagirait avec le repository. Cela permet de centraliser la logique métier et de rendre le contrôleur plus léger. - Validation des Données : Utiliser les annotations de validation de Jakarta Bean Validation (
@Valid,@NotNull,@Size, etc.) avec@Validatedsur l'entité reçue dans les requêtesPOSTetPUTpour s'assurer que les données sont valides avant de les traiter. - Gestion des Erreurs Globalisée : Utiliser
@ControllerAdviceet@ExceptionHandlerpour centraliser la gestion des exceptions et retourner des réponses d'erreur cohérentes et significatives (par exemple, 400 Bad Request pour les erreurs de validation, 404 Not Found si une ressource n'existe pas, 500 Internal Server Error pour des problèmes inattendus). - DTOs (Data Transfer Objects) : Pour les API complexes, il est souvent préférable d'utiliser des DTOs pour découpler votre modèle interne (entités JPA) des données exposées via l'API. Cela permet de masquer des champs internes, de combiner des données de plusieurs entités, ou de personnaliser la structure de la réponse/requête.
- Sécurité : Pour protéger votre API, vous devrez intégrer Spring Security pour l'authentification (JWT, OAuth2) et l'autorisation.
- Tests Unitaires et d'Intégration : Écrire des tests est crucial pour assurer la robustesse et la maintenabilité de votre API. Spring Boot facilite grandement l'écriture de tests pour les contrôleurs et les repositories.
- Versioning d'API : À mesure que votre API évolue, vous devrez gérer différentes versions pour éviter de casser les applications clientes existantes (ex:
/api/v1/tasks,/api/v2/tasks). - Documentation d'API : Utiliser des outils comme OpenAPI (Swagger) pour générer une documentation interactive de votre API, facilitant son utilisation par d'autres développeurs.
Conclusion
Félicitations ! Vous avez réussi à créer votre première API RESTful fonctionnelle avec Spring Boot. Vous avez appris les concepts fondamentaux de REST, comment configurer un projet Spring Boot, définir des entités JPA, interagir avec une base de données H2 via Spring Data JPA, et exposer des endpoints RESTful avec Spring MVC.
Ce projet simple est une base solide sur laquelle vous pouvez construire des applications backend beaucoup plus complexes. La maîtrise de Spring Boot pour le développement d'API RESTful est une compétence très recherchée dans l'industrie. Continuez à explorer les concepts avancés et les bonnes pratiques pour devenir un développeur backend Java expert. Votre parcours dans le Développement Backend Robuste avec Java et Spring Boot est désormais bien engagé !