What is CollectionModel<T> and how does it differ from returning a plain list? What does its HAL output look like?
answer
- wraps many + collection-level links
- items under _embedded, links under _links
- rel name derived from element type / @Relation
- T often EntityModel<X>
- step up to PagedModel for paging
basics
~10 sCollectionModel<T> wraps a collection of items so the whole collection can carry its own hypermedia links (like a self link). In HAL the items go under _embedded and collection-level links under _links.
solid answer
~40 s`CollectionModel<T>` is a `RepresentationModel` that holds many items plus collection-level links. A plain `List<Order>` can only serialize the items — it has nowhere to attach links describing the collection (self, next page, create action). `CollectionModel.of(items, linkTo(...).withSelfRel())` fixes that. `T` is usually `EntityModel<Order>` so each element also carries its own links, but it can be a plain type too. In HAL (`application/hal+json`), the elements render under an `_embedded` object keyed by a relation name (derived from the element type, e.g. `orderList`), and the collection's own links render under a top-level `_links`. You typically build it in a `RepresentationModelAssembler` via `toCollectionModel(...)`. It is the right return type for list endpoints that need hypermedia; for paged data you step up to `PagedModel`.
code
java · 17 linesimport org.springframework.hateoas.CollectionModel;
import org.springframework.hateoas.EntityModel;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
@GetMapping("/orders")
public CollectionModel<EntityModel<Order>> all() {
List<EntityModel<Order>> items = repository.findAll().stream()
.map(o -> EntityModel.of(o,
linkTo(methodOn(OrderController.class).one(o.getId())).withSelfRel()))
.toList();
return CollectionModel.of(items,
linkTo(methodOn(OrderController.class).all()).withSelfRel());
}
// Control the _embedded key on the element type:
// @Relation(collectionRelation = "orders")
// public class Order { ... }go deeper
Know it wraps many items so the collection can carry links; items go under _embedded.
Explain the type-derived rel, @Relation, and per-item vs collection-level links.
Drive it from an assembler and reason about empty-collection and rel-stability gotchas.
Treat the _embedded rel as a client contract and govern renames/versioning accordingly.
**Problem it solves.** A REST list endpoint often needs links that describe the *collection itself*, not just each item: a `self` link to the list, a `create` action, or pagination links. A raw `List<Order>` (or `List<EntityModel<Order>>`) is just a JSON array — an array has no place to hang links. `CollectionModel<T>` wraps the collection into an object so it can carry a `_links` block. **Type.** `CollectionModel<T> extends RepresentationModel<CollectionModel<T>>`. It holds an `Iterable<T>` of content plus links. The element type `T` is commonly `EntityModel<Order>` (so each item has its own links) but may be a plain domain type or DTO when per-item links aren't needed. **Factories.** - `CollectionModel.of(Iterable<T> content)` - `CollectionModel.of(Iterable<T> content, Link... links)` - `CollectionModel.empty()` for an empty collection (still a valid resource). **HAL serialization.** With `application/hal+json`: - The items are placed under `_embedded`, keyed by a *relation name*. By default the rel is derived from the element type name (e.g. a collection of `Order` → `orderList`). You can control it with `@Relation(collectionRelation = "orders")` on the element type, or via a `LinkRelationProvider`. - Collection-level links go under a top-level `_links`. Example: ``` { "_embedded": { "orderList": [ {"id":1, "_links":{...}}, ... ] }, "_links": { "self": {"href":"/orders"} } } ``` **Empty-collection gotcha.** With HAL, an empty `CollectionModel` may omit `_embedded` entirely (because there is no element type to derive the rel from), so clients should not assume the key is always present. Some teams use `CollectionModel.empty()` plus explicit rel via `withFallbackType`/typed empties to keep the key stable. **Building it.** The idiomatic path is a `RepresentationModelAssembler<Order, EntityModel<Order>>` whose default `toCollectionModel(Iterable<Order>)` maps each entity through `toModel` and wraps the result. You then `.add(...)` a self link. **When to use.** - Unpaged list endpoints needing hypermedia → `CollectionModel`. - Paged endpoints (page size / total / navigation) → `PagedModel` (a subclass). - Single item → `EntityModel`. **Gotchas.** - The `_embedded` rel name is type-derived; renaming the element class silently changes the client-visible key unless you pin it with `@Relation`. - `CollectionModel` gives collection-level links but does NOT add pagination metadata — that's `PagedModel`. - Returning `List<EntityModel<Order>>` gives per-item links but still no collection self link; wrap in `CollectionModel` to get both.
- Why is the `_embedded` key sometimes `orderList` and how do you make it `orders`?By default the collection relation is derived from the element type name via the LinkRelationProvider (e.g. `orderList`). Annotate the element type with `@Relation(collectionRelation = "orders")` to pin it.
- What breaks if you return `List<EntityModel<Order>>` instead of `CollectionModel`?You get per-item links but a bare JSON array — there is nowhere to attach collection-level links like the list's own `self` or pagination links.
saying these in an interview costs you the question
- Claiming CollectionModel adds pagination metadata (that's PagedModel)
- Assuming the `_embedded` key is always the same regardless of element type name
- Thinking a plain List can carry collection-level links