Skip to content
Spring Boot sb data-jpa 4 min read

Spring Data REST

Spring Data REST takes a Spring Data repository and automatically exposes it as a fully navigable hypermedia REST API — no controllers, no service layer, no mapping code. It inspects every JpaRepository in your context and publishes CRUD endpoints that speak HAL (Hypertext Application Language), complete with paging, sorting, and discoverable links between resources.

It is a fantastic accelerator for admin tools and CRUD-heavy backends, and a poor fit when you need bespoke request/response shapes or business logic. This page shows both sides.

Adding the starter

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

With the starter on the classpath and a repository like the one below, you already have a working API:

public interface ProductRepository extends JpaRepository<Product, Long> {
}

Boot the app and GET http://localhost:8080/ returns the root resource listing every exported repository:

{
  "_links": {
    "products": {
      "href": "http://localhost:8080/products{?page,size,sort}",
      "templated": true
    },
    "profile": { "href": "http://localhost:8080/profile" }
  }
}

The collection name is derived from the entity (Productproducts). Each item gets a self link and links to its associations.

CRUD out of the box

Every standard verb is mapped automatically:

HTTPPathAction
GET/productsPaged collection
GET/products/{id}Single resource
POST/productsCreate
PUT / PATCH/products/{id}Replace / partial update
DELETE/products/{id}Delete
GET/products/searchList exported query methods

A GET /products/42 returns HAL, with _links letting a client walk to related resources:

{
  "name": "Mechanical Keyboard",
  "price": 89.99,
  "_links": {
    "self": { "href": "http://localhost:8080/products/42" },
    "product": { "href": "http://localhost:8080/products/42" },
    "category": { "href": "http://localhost:8080/products/42/category" }
  }
}

Notice the entity id is not in the JSON body — it lives in the self link. Associations (here category) are exposed as their own linkable sub-resources rather than inlined.

Exposing query methods

Any derived query method on the repository is published under /search:

public interface ProductRepository extends JpaRepository<Product, Long> {

    List<Product> findByCategory(@Param("category") String category);
}
GET /products/search/findByCategory?category=books

Tip: Use @Param on query-method arguments. Spring Data REST relies on it to map request parameters to method arguments — without it, parameter names may be lost after compilation.

Customising with @RepositoryRestResource

The defaults are conventions you can override. @RepositoryRestResource controls the exported path and link relation, and lets you hide methods or whole repositories.

@RepositoryRestResource(path = "items", collectionResourceRel = "items")
public interface ProductRepository extends JpaRepository<Product, Long> {

    // Hide a single method from the REST API
    @RestResource(exported = false)
    @Override
    void deleteById(Long id);

    @RestResource(path = "by-category", rel = "by-category")
    List<Product> findByCategory(@Param("category") String category);
}
  • path / collectionResourceRel rename the URL segment and HAL link key (/items instead of /products).
  • @RestResource(exported = false) removes a method — here DELETE is disabled.
  • To hide an entire repository, annotate it with @RepositoryRestResource(exported = false).

Projections — reshaping a single resource

By default the full entity is serialized. A projection is an interface that selects or computes a subset of fields, requested via ?projection=:

@Projection(name = "summary", types = Product.class)
public interface ProductSummary {

    String getName();
    BigDecimal getPrice();

    // Computed (SpEL) property
    @Value("#{target.name + ' (' + target.category.name + ')'}")
    String getLabel();
}
GET /products/42?projection=summary
{
  "name": "Mechanical Keyboard",
  "price": 89.99,
  "label": "Mechanical Keyboard (Peripherals)"
}

Excerpts — projections on the collection

An excerpt applies a projection automatically to every item in a collection so the list endpoint returns a lighter shape without a query parameter:

@RepositoryRestResource(excerptProjection = ProductSummary.class)
public interface ProductRepository extends JpaRepository<Product, Long> {
}

Now GET /products embeds the summary view for each item under _embedded.products, keeping list payloads small.

Note: Excerpts only apply to collection resources, not to the single-item endpoint. Requesting /products/42 still returns the full entity unless you add ?projection=summary.

Configuration

Tune behaviour with the spring.data.rest.* properties:

spring:
  data:
    rest:
      base-path: /api          # all endpoints under /api/products
      default-page-size: 20
      max-page-size: 100
      return-body-on-create: true
      return-body-on-update: true

For finer control, implement RepositoryRestConfigurer:

@Configuration
public class RestConfig implements RepositoryRestConfigurer {

    @Override
    public void configureRepositoryRestConfiguration(
            RepositoryRestConfiguration config, CorsRegistry cors) {
        config.setBasePath("/api");
        // Expose entity ids in the JSON body for this type
        config.exposeIdsFor(Product.class);
    }
}

When it shines vs when to hand-write controllers

Use Spring Data REST whenHand-write REST controllers when
Building an internal admin / CRUD consoleYou need custom DTO request/response shapes
The API maps closely to the entity modelBusiness logic must run on create/update
You want HATEOAS discoverability for freeThe contract must be stable and decoupled from entities
Rapid prototyping over an existing schemaYou need fine-grained authorization per operation

Warning: Spring Data REST exposes your entities directly, leaking persistence concerns into the public contract. For external, versioned APIs prefer explicit controllers with DTOs and a mapping layer. Reserve Data REST for trusted, internal CRUD.

You can also mix the two: keep Data REST for the boring CRUD and add normal @RestController endpoints for the handful of operations that need real logic.

Last updated June 13, 2026
Was this helpful?