skip to content

How do @Projection interfaces and an excerptProjection change the exposed representation in Spring Data REST?

level: middleimportance: must knowfreq 55%

answer

  1. @Projection(name, types) interface of getters
  2. ?projection=name opt-in
  3. excerptProjection = default for _embedded ONLY
  4. NOT applied to single item resource
  5. closed=column-select, open=@Value loads all

basics

~10 s

A @Projection is an interface selecting/reshaping which fields a resource exposes. You request it with ?projection=name. Setting it as excerptProjection on the repository makes it the default view for items inside collections (_embedded).

solid answer

~40 s

A projection is a Java interface annotated with @Projection(name="...", types=Entity.class) whose getter methods define which properties are exposed and can flatten or rename them. Clients opt in per request with ?projection=name. Declaring it on @RepositoryRestResource(excerptProjection = SomeProjection.class) makes it the automatic representation for that entity when it appears inside a collection's _embedded block (and for association resources) — but crucially NOT for the single-item resource, which still returns the full entity unless the client explicitly adds ?projection. Projections are auto-registered if they live in (a sub-package of) the entity's package, or manually via RepositoryRestConfiguration's projection config. Closed projections (plain getters) let JPA fetch only needed columns; open projections using @Value SpEL fetch the whole entity.

code

java · 12 lines
java
@Projection(name = "summary", types = Book.class)
public interface BookSummary {
    String getTitle();
    @Value("#{target.author.name}")   // open projection -> loads full entity
    String getAuthorName();
}

@RepositoryRestResource(excerptProjection = BookSummary.class)
public interface BookRepository extends JpaRepository<Book, Long> { }
// GET /books        -> _embedded.books[*] use BookSummary automatically
// GET /books/1      -> FULL Book (excerpt NOT applied)
// GET /books/1?projection=summary -> BookSummary

go deeper

for a junior

Knows a projection reshapes fields and is requested with ?projection.

for a middle

Explains excerptProjection applies to _embedded but not single items, and how registration works.

for a senior

Distinguishes closed vs open projections and their query cost; uses excerpts to inline associations deliberately.

for a principal

Weighs excerpt over-fetch on large collections, read/write representation divergence, and API-evolution implications of projection contracts.

**Projections** let you decouple the *exposed representation* from the JPA entity's full shape — hiding fields, adding computed values, or inlining related data. **Defining a projection:** ```java @Projection(name = "summary", types = { Book.class }) public interface BookSummary { String getTitle(); @Value("#{target.author.name}") String getAuthorName(); } ``` It is a plain interface. Its accessor methods name the entity properties to expose. Anything not listed is omitted from the representation. **Closed vs open projections:** - **Closed projection** — every getter maps directly to an entity property (e.g. `getTitle()` → `title`). Spring Data can then generate an optimised query selecting only those columns. - **Open projection** — a getter is annotated with `@Value("#{target.author.name}")` (SpEL). This computes values or reaches into associations, but Spring Data must load the *entire* backing entity, losing the column-selection optimisation. **Registration:** A projection is discovered automatically if it lives in the **same package as, or a sub-package of, the entity** it targets. Otherwise you register it explicitly: ```java config.getProjectionConfiguration().addProjection(BookSummary.class); ``` inside a `RepositoryRestConfigurer`. **Requesting a projection:** Clients add `?projection=summary` to the URL. The response then uses that shape. Spring Data REST advertises available projections in the `profile` metadata. **excerptProjection — the key exam point:** ```java @RepositoryRestResource(excerptProjection = BookSummary.class) public interface BookRepository extends JpaRepository<Book, Long> { } ``` An **excerpt projection** is the projection applied *by default* when a resource appears **inside a collection** — i.e. every item under `_embedded.books` uses `BookSummary` automatically, without `?projection`. It is also used when the entity appears as an association resource. The **major gotcha**: the excerpt is deliberately **NOT** applied to the *single-item* resource (`GET /books/1`) to avoid the client mistaking a partial view for the full editable resource. To get the excerpt on a single item you must explicitly request `GET /books/1?projection=summary`. **Additional gotchas:** - Excerpts add value by *inlining associations* into collection responses (fewer round-trips) — a common motivation is to embed `author.name` directly instead of forcing a follow-up call per book. - Projections are **read-only** shaping; they do not affect writes (PUT/PATCH still target the full entity). - A projection must be an interface (class-based DTO projections exist in Spring Data query methods but Spring Data REST projection resolution expects the `@Projection` interface). - If an excerpt inlines heavy associations for a large collection, you can reintroduce the N+1 / over-fetch problem you were trying to avoid. **When to use:** projections for tailored read views (mobile summary vs full detail); excerptProjection specifically to enrich collection listings so clients don't have to follow every association link.

  • Why isn't the excerpt projection applied to the single-item resource by default?
    To prevent clients treating a partial excerpt as the complete, editable resource. Item resources must reflect the full state used for PUT/PATCH; you opt into the excerpt on an item explicitly via ?projection.
  • What is the performance difference between a closed and an open projection?
    A closed projection (getters matching entity properties) lets Spring Data issue a query selecting only those columns. An open projection using @Value SpEL must materialise the whole entity, forfeiting that optimisation.
  • Where must a projection interface live to be auto-registered?
    In the same package as, or a sub-package of, the entity it targets. Otherwise register it via config.getProjectionConfiguration().addProjection(...).

saying these in an interview costs you the question

  • Claiming the excerptProjection also applies to GET /books/1 by default
  • Thinking projections change what PUT/PATCH writes
  • Believing open (@Value) projections are as query-efficient as closed ones
  • Assuming any interface is picked up regardless of package location

context