Spring HATEOAS - Quick Reference
Full Reference: See advanced.md for affordances (HAL-FORMS), embedded resources, link relations, media types, and testing patterns.
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: spring-hateoas for comprehensive documentation.
Dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency>
Core Concepts
Richardson Maturity Model: Level 0: Plain Old XML/JSON (single endpoint) Level 1: Resources (multiple endpoints) Level 2: HTTP Verbs (GET, POST, PUT, DELETE) Level 3: Hypermedia Controls (HATEOAS) ← This!
EntityModel Wrapper
@GetMapping("/users/{id}") public EntityModel<User> getUser(@PathVariable Long id) { User user = userService.findById(id);
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("users"),
linkTo(methodOn(OrderController.class).getOrdersByUser(id)).withRel("orders")
);
}
Response Format (HAL)
{ "id": 1, "name": "John Doe", "_links": { "self": { "href": "http://localhost:8080/api/users/1" }, "users": { "href": "http://localhost:8080/api/users" }, "orders": { "href": "http://localhost:8080/api/users/1/orders" } } }
CollectionModel
@GetMapping("/users") public CollectionModel<EntityModel<User>> getAllUsers() { List<User> users = userService.findAll();
List<EntityModel<User>> userModels = users.stream()
.map(user -> EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfRel()
))
.toList();
return CollectionModel.of(userModels,
linkTo(methodOn(UserController.class).getAllUsers()).withSelfRel()
);
}
RepresentationModelAssembler
@Component public class UserModelAssembler implements RepresentationModelAssembler<User, EntityModel<User>> {
@Override
public EntityModel<User> toModel(User user) {
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("users")
);
}
}
// Usage @RestController @RequestMapping("/api/users") @RequiredArgsConstructor public class UserController { private final UserService userService; private final UserModelAssembler assembler;
@GetMapping("/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
return assembler.toModel(userService.findById(id));
}
@GetMapping
public CollectionModel<EntityModel<User>> getAllUsers() {
return assembler.toCollectionModel(userService.findAll());
}
}
Pagination Support
@GetMapping("/users") public PagedModel<EntityModel<User>> getAllUsers( @PageableDefault(size = 20) Pageable pageable, PagedResourcesAssembler<User> pagedAssembler) {
Page<User> users = userService.findAll(pageable);
return pagedAssembler.toModel(users, userModelAssembler);
}
Best Practices
Do Don't
Use ModelAssembler pattern Build links inline everywhere
Include self links always Omit navigation links
Use standard IANA relations Invent new relation names
Add conditional links for actions Show all links regardless of state
When NOT to Use This Skill
-
Simple REST APIs - If clients don't need hypermedia navigation
-
GraphQL APIs - Use spring-graphql instead
-
Internal microservices - Often unnecessary overhead
Anti-Patterns
Anti-Pattern Problem Solution
Building links inline Duplicated code everywhere Use RepresentationModelAssembler
Missing self links Clients can't identify resources Always add withSelfRel()
Hardcoded URLs Breaks on deployment Use linkTo/methodOn builders
Inventing relations Non-standard, hard to understand Use IANA link relations
Quick Troubleshooting
Problem Diagnostic Fix
Links not serialized Check Accept header Ensure HAL media type
NullPointerException in linkTo Check controller method Verify method signature matches
Wrong base URL Check proxy config Configure server.forward-headers-strategy
Pagination links missing Check assembler Use PagedResourcesAssembler
Production Checklist
-
ModelAssemblers for all resources
-
Self links on every resource
-
Collection links from items
-
Pagination with navigation links
-
Conditional action links
-
IANA link relations where possible
Reference Documentation
- Spring HATEOAS Reference