Spring Data REST - créer des API sans controllers

Publié le 17/03/2026 Source : sfeir.dev

Spring Data REST - créer des API sans controllers

Créer une API REST en Spring Boot, c’est souvent une succession de contrôleurs, DTO, services, tests, documentation. C’est robuste, mais pas toujours nécessaire.

Spring Data REST propose une alternative simple : exposer directement les repositories Spring Data sous forme d’API REST, sans écrire un seul controller. Le gain de temps est réel, à condition d’accepter un contrat plus implicite.

Dans cet article, on construit un exemple complet, puis on pose clairement les limites : quand c’est un super outil, et quand il faut revenir à des controllers classiques.

Quand Spring Data REST est pertinent

Et à l’inverse : dès que le contrat API doit être très précis, versionné ou fortement sécurisé, on bascule plus souvent sur des controllers et des DTO.

Mise en place minimale

Avec Spring Boot, il suffit d’ajouter le starter Spring Data REST (souvent déjà présent via spring-boot-starter-data-jpa).

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

Un exemple concret

On part d’un domaine ultra simple : Book et Author.

@Entity
public class Author {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    // getters/setters
}

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    private Author author;

    // getters/setters
}

Ensuite, un repository suffit.

@RepositoryRestResource(path = "books")
public interface BookRepository extends JpaRepository<Book, Long> {

    List<Book> findByTitleContainingIgnoreCase(String title);
}

Spring Data REST expose automatiquement :

Le tout en HAL, avec pagination et liens hypermédia intégrés.

Mode lecture seule

Il est courant d’exposer certains repositories en lecture seule.

Option 1 : désactiver les méthodes au niveau du repository

@RepositoryRestResource(path = "books")
public interface BookRepository extends JpaRepository<Book, Long> {

    @Override
    @RestResource(exported = false)
    <S extends Book> S save(S entity);

    @Override
    @RestResource(exported = false)
    void deleteById(Long id);
}

Option 2 : configurer l’exposition globalement

@Configuration
public class RestExposureConfig implements RepositoryRestConfigurer {

    @Override
    public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
        config.getExposureConfiguration()
                .forDomainType(Book.class)
                .withItemExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE))
                .withCollectionExposure((metadata, httpMethods) -> httpMethods.disable(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE));
    }
}

Cette approche est utile pour des catalogues ou des référentiels.

Lecture / écriture contrôlée

Si l’API est publique, on veut souvent limiter ce qui est exposé :

Spring Data REST offre plusieurs leviers :

Exemple de projection :

@Projection(name = "summary", types = Book.class)
public interface BookSummary {
    String getTitle();
    Author getAuthor();
}

Appel :

GET /books?projection=summary

{
    "_embedded": {
        "books": [
            {
                "title": "Refactoring",
                "_links": {
                    "self": {
                        "href": "http://localhost:8094/api/books/1"
                    },
                    "book": {
                        "href": "http://localhost:8094/api/books/1"
                    },
                    "author": {
                        "href": "http://localhost:8094/api/books/1/author"
                    }
                }
            },
            {
                "title": "Clean Code",
                "_links": {
                    "self": {
                        "href": "http://localhost:8094/api/books/2"
                    },
                    "book": {
                        "href": "http://localhost:8094/api/books/2"
                    },
                    "author": {
                        "href": "http://localhost:8094/api/books/2/author"
                    }
                }
            },
            {
                "title": "Effective Java",
                "_links": {
                    "self": {
                        "href": "http://localhost:8094/api/books/3"
                    },
                    "book": {
                        "href": "http://localhost:8094/api/books/3"
                    },
                    "author": {
                        "href": "http://localhost:8094/api/books/3/author"
                    }
                }
            },
            {
                "title": "Clean Architecture",
                "_links": {
                    "self": {
                        "href": "http://localhost:8094/api/books/4"
                    },
                    "book": {
                        "href": "http://localhost:8094/api/books/4"
                    },
                    "author": {
                        "href": "http://localhost:8094/api/books/4/author"
                    }
                }
            },
            {
                "title": "test",
                "_links": {
                    "self": {
                        "href": "http://localhost:8094/api/books/5"
                    },
                    "book": {
                        "href": "http://localhost:8094/api/books/5"
                    },
                    "author": {
                        "href": "http://localhost:8094/api/books/5/author"
                    }
                }
            }
        ]
    },
    "_links": {
        "self": {
            "href": "http://localhost:8094/api/books?projection=summary&page=0&size=20"
        },
        "profile": {
            "href": "http://localhost:8094/api/profile/books"
        },
        "search": {
            "href": "http://localhost:8094/api/books/search"
        }
    },
    "page": {
        "size": 20,
        "totalElements": 5,
        "totalPages": 1,
        "number": 0
    }
}

Documentation OpenAPI / Swagger

Oui, c’est compatible. Il suffit d’ajouter le starter Springdoc qui comprend Spring Data REST.

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

On retrouve ensuite les endpoints dans Swagger UI (/swagger-ui.html).

C’est particulièrement utile si on a déjà lu Réussir sa migration de Swagger 2 à OpenApi 3.

Ce que Spring Data REST ne fait pas bien

En clair : c’est excellent pour du CRUD rapide, mais pas pour un contrat API public strict.

En résumé

Spring Data REST permet d’obtenir une API complète en quelques minutes, sans controllers, avec pagination, recherche, et documentation automatique. C’est un accélérateur net pour un back-office ou un produit interne.

Mais si vous cherchez un contrat stable, versionné, très contrôlé ou optimisé pour un client spécifique, un controller reste souvent la bonne solution.

Et comme toujours : on choisit l’outil, pas l’inverse.