Skip to content

Your First Project

Ce guide vous accompagne pas à pas dans la création d'un projet complet avec SpringFlow. Vous allez créer une API de Blog avec des auteurs et des articles en moins de 15 minutes.

Ce que vous allez construire

Une API REST complète avec:

  • Authors - Gestion des auteurs
  • BlogPosts - Gestion des articles de blog
  • Relation - Un auteur peut avoir plusieurs articles
  • Endpoints CRUD - Automatiquement générés
  • Swagger UI - Documentation interactive
  • Validation - Règles métier intégrées

Créer le Projet Spring Boot

Option 1: Spring Initializr (Recommandé)

  1. Allez sur start.spring.io

  2. Configurez votre projet:

  3. Project: Maven
  4. Language: Java
  5. Spring Boot: 3.2.1+
  6. Java: 17

  7. Remplissez les métadonnées:

  8. Group: com.example
  9. Artifact: blog-api
  10. Name: blog-api
  11. Package name: com.example.blogapi

  12. Ajoutez les dépendances:

  13. Spring Web
  14. Spring Data JPA
  15. H2 Database
  16. Validation
  17. Lombok

  18. Cliquez sur Generate et décompressez le fichier téléchargé

Option 2: Ligne de commande

curl https://start.spring.io/starter.tgz \
  -d dependencies=web,data-jpa,h2,validation,lombok \
  -d javaVersion=17 \
  -d bootVersion=3.2.1 \
  -d type=maven-project \
  -d groupId=com.example \
  -d artifactId=blog-api \
  -d name=blog-api \
  -d packageName=com.example.blogapi \
  -d baseDir=blog-api \
  | tar -xzvf -

cd blog-api

Ajouter SpringFlow

Ouvrez pom.xml et ajoutez la dépendance SpringFlow dans la section <dependencies>:

<dependency>
    <groupId>io.github.tky0065</groupId>
    <artifactId>springflow-starter</artifactId>
    <version>0.5.1</version>
</dependency>

Version actuelle

Vérifiez la dernière version sur Maven Central

Configurer la Base de Données

Créez/modifiez src/main/resources/application.yml:

spring:
  application:
    name: blog-api

  # Configuration H2 (base en mémoire pour le développement)
  datasource:
    url: jdbc:h2:mem:blogdb
    driver-class-name: org.h2.Driver
    username: sa
    password:

  # Configuration JPA
  jpa:
    hibernate:
      ddl-auto: create-drop  # Recrée les tables au démarrage
    show-sql: true           # Affiche les requêtes SQL
    properties:
      hibernate:
        format_sql: true
    defer-datasource-initialization: true

  # Console H2 (pour inspecter la base)
  h2:
    console:
      enabled: true
      path: /h2-console

# Configuration SpringFlow
springflow:
  enabled: true
  base-path: /api

  pagination:
    default-page-size: 20
    max-page-size: 100

  swagger:
    enabled: true
    title: Blog API
    description: API de gestion de blog avec SpringFlow
    version: 1.0.0
    contact-name: Votre Nom
    contact-email: votre.email@example.com

# Configuration logging
logging:
  level:
    io.springflow: DEBUG
    org.hibernate.SQL: DEBUG

Base de données H2

H2 est une base de données en mémoire parfaite pour le développement. Pour la production, remplacez par PostgreSQL, MySQL, etc.

Créer les Entités

Entité Author

Créez src/main/java/com/example/blogapi/entity/Author.java:

package com.example.blogapi.entity;

import io.springflow.annotations.AutoApi;
import io.springflow.annotations.Hidden;
import io.springflow.annotations.ReadOnly;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name = "authors")
@Data
@NoArgsConstructor
@AllArgsConstructor
@AutoApi(
    path = "/authors",
    description = "Author management API",
    tags = {"Authors"}
)
public class Author {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "Name is required")
    @Size(min = 2, max = 100, message = "Name must be between 2 and 100 characters")
    @Column(nullable = false)
    private String name;

    @Email(message = "Email must be valid")
    @NotBlank(message = "Email is required")
    @Column(nullable = false, unique = true)
    private String email;

    @Size(max = 500)
    private String bio;

    @Column(name = "twitter_handle")
    private String twitterHandle;

    @Hidden  // Ne sera jamais exposé dans l'API
    @Column(nullable = false)
    private String apiKey = generateApiKey();

    @ReadOnly  // Visible en lecture, non modifiable
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @ReadOnly
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    // Relation: Un auteur peut avoir plusieurs articles
    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<BlogPost> posts = new ArrayList<>();

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }

    private String generateApiKey() {
        return "AK-" + System.currentTimeMillis() + "-" +
               (int)(Math.random() * 10000);
    }
}

Entité BlogPost

Créez src/main/java/com/example/blogapi/entity/BlogPost.java:

package com.example.blogapi.entity;

import io.springflow.annotations.AutoApi;
import io.springflow.annotations.ReadOnly;
import io.springflow.annotations.Filterable;
import io.springflow.annotations.FilterType;
import jakarta.persistence.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.time.LocalDateTime;

@Entity
@Table(name = "blog_posts")
@Data
@NoArgsConstructor
@AllArgsConstructor
@AutoApi(
    path = "/posts",
    description = "Blog post management API",
    tags = {"Blog Posts"}
)
public class BlogPost {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "Title is required")
    @Size(min = 5, max = 200, message = "Title must be between 5 and 200 characters")
    @Filterable(types = {FilterType.LIKE, FilterType.EQUALS})
    @Column(nullable = false)
    private String title;

    @Size(max = 500)
    private String summary;

    @NotBlank(message = "Content is required")
    @Size(min = 10, message = "Content must be at least 10 characters")
    @Column(columnDefinition = "TEXT")
    private String content;

    @Filterable(types = {FilterType.EQUALS, FilterType.IN})
    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private PostStatus status = PostStatus.DRAFT;

    @Filterable(types = {FilterType.EQUALS, FilterType.LIKE})
    private String category;

    @Column(name = "view_count")
    private Integer viewCount = 0;

    // Relation: Chaque article appartient à un auteur
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "author_id", nullable = false)
    private Author author;

    @ReadOnly
    @Column(name = "published_at")
    private LocalDateTime publishedAt;

    @ReadOnly
    @Column(name = "created_at", nullable = false, updatable = false)
    private LocalDateTime createdAt;

    @ReadOnly
    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
        updatedAt = LocalDateTime.now();
        if (status == PostStatus.PUBLISHED && publishedAt == null) {
            publishedAt = LocalDateTime.now();
        }
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
        if (status == PostStatus.PUBLISHED && publishedAt == null) {
            publishedAt = LocalDateTime.now();
        }
    }

    // Enum pour le statut
    public enum PostStatus {
        DRAFT,
        PUBLISHED,
        ARCHIVED
    }
}

Ajouter des Données de Test (Optionnel)

Créez src/main/resources/data.sql:

-- Insérer des auteurs
INSERT INTO authors (name, email, bio, twitter_handle, api_key, created_at, updated_at)
VALUES
('Alice Johnson', 'alice@example.com', 'Passionnée de technologie et développement web', '@alicejohnson', 'AK-1234567890-5678', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('Bob Smith', 'bob@example.com', 'Expert Java et architecture logicielle', '@bobsmith', 'AK-1234567891-9012', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),
('Carol Davis', 'carol@example.com', 'Développeuse full-stack et blogueuse tech', '@caroldavis', 'AK-1234567892-3456', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);

-- Insérer des articles
INSERT INTO blog_posts (title, summary, content, status, category, view_count, author_id, published_at, created_at, updated_at)
VALUES
('Introduction à SpringFlow', 'Découvrez comment SpringFlow simplifie le développement REST', 'SpringFlow est un framework révolutionnaire qui génère automatiquement des APIs REST complètes à partir de vos entités JPA. Plus besoin de créer manuellement les repositories, services et controllers!', 'PUBLISHED', 'Tutorial', 150, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),

('Les meilleures pratiques Java 17', 'Guide complet des nouveautés Java 17', 'Java 17 apporte de nombreuses améliorations comme les records, les sealed classes, et le pattern matching. Découvrez comment les utiliser dans vos projets.', 'PUBLISHED', 'Java', 320, 2, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),

('Microservices avec Spring Boot', 'Architecture microservices moderne', 'Les microservices sont devenus la norme pour les applications modernes. Ce guide vous montre comment créer une architecture microservices robuste avec Spring Boot.', 'DRAFT', 'Architecture', 0, 2, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),

('Guide complet de Spring Data JPA', 'Maîtrisez JPA avec Spring', 'Spring Data JPA simplifie énormément l''accès aux données. Apprenez les concepts avancés comme les specifications, les projections, et les requêtes natives.', 'PUBLISHED', 'Spring', 275, 3, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP),

('Docker pour les développeurs', 'Containerisez vos applications facilement', 'Docker révolutionne le déploiement d''applications. Découvrez comment créer des containers optimisés pour vos applications Spring Boot.', 'DRAFT', 'DevOps', 0, 1, NULL, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);

Lancer l'Application

Démarrer le serveur

./mvnw spring-boot:run

Ou avec Gradle:

./gradlew bootRun

Vérifier les logs

Vous devriez voir dans les logs:

INFO  AutoApiRepositoryRegistrar - Starting AutoApi Repository, Service, and Controller Registration...
INFO  AutoApiRepositoryRegistrar - Registered repository for Author
INFO  AutoApiRepositoryRegistrar - Registered service for Author
INFO  AutoApiRepositoryRegistrar - Registered controller for Author
INFO  AutoApiRepositoryRegistrar - Registered repository for BlogPost
INFO  AutoApiRepositoryRegistrar - Registered service for BlogPost
INFO  AutoApiRepositoryRegistrar - Registered controller for BlogPost
INFO  AutoApiRepositoryRegistrar - AutoApi registration completed. Registered 2 entities.

Tester votre API

Accéder à Swagger UI

Ouvrez votre navigateur: http://localhost:8080/swagger-ui.html

Vous verrez tous vos endpoints générés automatiquement:

Authors: - GET /api/authors - Liste des auteurs - GET /api/authors/{id} - Détails d'un auteur - POST /api/authors - Créer un auteur - PUT /api/authors/{id} - Modifier un auteur - DELETE /api/authors/{id} - Supprimer un auteur

Blog Posts: - GET /api/posts - Liste des articles - GET /api/posts/{id} - Détails d'un article - POST /api/posts - Créer un article - PUT /api/posts/{id} - Modifier un article - DELETE /api/posts/{id} - Supprimer un article

Tester avec cURL

Créer un auteur

curl -X POST http://localhost:8080/api/authors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "David Wilson",
    "email": "david@example.com",
    "bio": "Développeur passionné",
    "twitterHandle": "@davidwilson"
  }'

Réponse:

{
  "id": 4,
  "name": "David Wilson",
  "email": "david@example.com",
  "bio": "Développeur passionné",
  "twitterHandle": "@davidwilson",
  "createdAt": "2025-12-26T15:30:00",
  "updatedAt": "2025-12-26T15:30:00"
}

Notez que apiKey est caché

Le champ apiKey marqué avec @Hidden n'apparaît jamais dans la réponse!

Lister tous les auteurs

curl http://localhost:8080/api/authors

Lister avec pagination

curl "http://localhost:8080/api/authors?page=0&size=10&sort=name,asc"

Créer un article

curl -X POST http://localhost:8080/api/posts \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Mon premier article SpringFlow",
    "summary": "Un article test",
    "content": "Contenu de mon premier article créé avec SpringFlow",
    "status": "PUBLISHED",
    "category": "Tutorial",
    "author": {
      "id": 1
    }
  }'

Filtrer les articles publiés

curl "http://localhost:8080/api/posts?status=PUBLISHED"

Rechercher par titre

curl "http://localhost:8080/api/posts?title=SpringFlow"

Console H2

Accédez à la console H2: http://localhost:8080/h2-console

  • JDBC URL: jdbc:h2:mem:blogdb
  • Username: sa
  • Password: (vide)

Exécutez des requêtes SQL directement:

SELECT * FROM authors;
SELECT * FROM blog_posts;
SELECT bp.title, a.name AS author
FROM blog_posts bp
JOIN authors a ON bp.author_id = a.id;

Validation en Action

Tentez de créer un auteur invalide

curl -X POST http://localhost:8080/api/authors \
  -H "Content-Type: application/json" \
  -d '{
    "name": "A",
    "email": "invalid-email"
  }'

Réponse (erreur 400):

{
  "timestamp": "2025-12-26T15:35:00Z",
  "status": 400,
  "error": "Bad Request",
  "message": "Validation failed",
  "validationErrors": [
    {
      "field": "name",
      "message": "Name must be between 2 and 100 characters",
      "rejectedValue": "A",
      "code": "Size"
    },
    {
      "field": "email",
      "message": "Email must be valid",
      "rejectedValue": "invalid-email",
      "code": "Email"
    }
  ]
}

Personnalisation (Optionnel)

Ajouter un Service Personnalisé

Si vous voulez ajouter une logique métier custom pour BlogPost:

Créez src/main/java/com/example/blogapi/service/BlogPostService.java:

package com.example.blogapi.service;

import com.example.blogapi.entity.BlogPost;
import io.springflow.core.service.GenericCrudService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Service;

@Service
public class BlogPostService extends GenericCrudService<BlogPost, Long> {

    private static final Logger log = LoggerFactory.getLogger(BlogPostService.class);

    public BlogPostService(@Qualifier("blogPostRepository") JpaRepository<BlogPost, Long> repository) {
        super(repository, BlogPost.class);
    }

    @Override
    protected void beforeCreate(BlogPost post) {
        log.info("Creating new blog post: {}", post.getTitle());

        // Validation custom: titre unique
        if (repository.findAll().stream()
                .anyMatch(existing -> existing.getTitle().equalsIgnoreCase(post.getTitle()))) {
            throw new IllegalArgumentException("A post with this title already exists");
        }

        // Générer un slug à partir du titre
        post.setSlug(generateSlug(post.getTitle()));
    }

    @Override
    protected void afterCreate(BlogPost post) {
        log.info("Blog post created successfully with ID: {}", post.getId());
        // Envoyer notification, invalider cache, etc.
    }

    private String generateSlug(String title) {
        return title.toLowerCase()
                .replaceAll("[^a-z0-9\\s-]", "")
                .replaceAll("\\s+", "-")
                .replaceAll("-+", "-")
                .trim();
    }
}

SpringFlow détectera automatiquement BlogPostService

SpringFlow verra que vous avez un bean blogPostService et utilisera votre implémentation au lieu d'en générer une!

Récapitulatif

En quelques étapes simples, vous avez créé:

✅ Un projet Spring Boot complet ✅ 2 entités JPA avec relations ✅ 10 endpoints REST automatiquement générés ✅ Validation des données ✅ Documentation Swagger interactive ✅ Données de test ✅ Gestion des champs cachés et en lecture seule

Tout ça sans écrire un seul Repository, Service ou Controller!

Prochaines Étapes

Fonctionnalités Avancées

  1. Filtrage Dynamique - Guide de filtrage

    GET /api/posts?status=PUBLISHED&category=Tutorial
    

  2. Sécurité - Guide de sécurité

    springflow:
      security:
        enabled: true
        default-level: AUTHENTICATED
    

  3. Composants Personnalisés - Documentation complète

  4. Repositories custom avec requêtes complexes
  5. Services custom avec logique métier
  6. Controllers custom avec endpoints spécifiques

  7. Soft Delete - Guide soft delete

    @SoftDelete
    private Boolean deleted = false;
    

  8. Audit Trail - Guide auditing

    @Auditable
    @CreatedBy
    private String createdBy;
    

Migration vers Production

Pour passer en production:

  1. Remplacer H2 par une vraie base de données:
# application-prod.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/blogdb
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate  # Ne pas recréer les tables!
  1. Ajouter la sécurité:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
  1. Configurer les CORS si nécessaire:
@Configuration
public class CorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                        .allowedOrigins("https://monsite.com")
                        .allowedMethods("GET", "POST", "PUT", "DELETE");
            }
        };
    }
}
  1. Activer les métriques:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Besoin d'Aide?

Ressources Supplémentaires


Félicitations! Vous avez créé votre première API avec SpringFlow. Le vrai pouvoir de SpringFlow se révèle quand vous réalisez que vous pouvez ajouter 10 nouvelles entités et obtenir instantanément 50+ endpoints REST complets! 🚀