What is HAL in Spring HATEOAS, and how does a HAL JSON response differ from a plain JSON response?
answer
- _links + _embedded
- application/hal+json
- EntityModel / CollectionModel wrap the POJO
- linkTo(methodOn(...)).withSelfRel()
- starter-hateoas auto-enables
basics
~20 sHAL (Hypertext Application Language) is a JSON format that adds hyperlinks to a resource under a _links object, plus embedded related resources under _embedded. Its media type is application/hal+json. Plain JSON has only data, no links.
solid answer
~40 sHAL is a standardized hypermedia JSON format Spring HATEOAS uses to make REST responses navigable. Beyond the resource's own fields, a HAL document carries a `_links` object (each entry has an `href`, keyed by relation name like `self` or `orders`) and optionally an `_embedded` object holding related resources inline. Its media type is `application/hal+json`, and Spring Boot's `spring-boot-starter-hateoas` auto-enables HAL rendering. You build these responses by wrapping domain objects in `EntityModel<T>` or `CollectionModel<T>` and adding links via `WebMvcLinkBuilder.linkTo(methodOn(...)).withSelfRel()`. The value is discoverability: clients follow links by relation name instead of hard-coding URI templates, so the server can change URIs without breaking clients (the HATEOAS constraint of REST).
code
java · 23 linesimport org.springframework.hateoas.EntityModel;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;
@RestController
class EmployeeController {
@GetMapping("/employees/{id}")
EntityModel<Employee> one(@PathVariable Long id) {
Employee employee = repository.findById(id).orElseThrow();
return EntityModel.of(employee,
linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel(),
linkTo(methodOn(EmployeeController.class).all()).withRel("employees"));
}
}
// Produces:
// {
// "id": 1, "name": "Ada",
// "_links": {
// "self": { "href": "http://localhost/employees/1" },
// "employees": { "href": "http://localhost/employees" }
// }
// }go deeper
Know that HAL adds _links/_embedded, the media type is application/hal+json, and you wrap objects in EntityModel.
Should build links with WebMvcLinkBuilder.linkTo(methodOn(...)) and understand relation-vs-URI decoupling.
Explains auto-configuration via the starter, PagedModel, and the read-only limitation of HAL that motivates HAL-FORMS.
Frames HAL as one hypermedia choice among several (HAL-FORMS, Collection+JSON, UBER) and weighs client-coupling tradeoffs for API longevity.
**HATEOAS** stands for *Hypermedia As The Engine Of Application State* — the REST constraint that a response should tell the client what it can do next via links, rather than the client hard-coding URIs. **Spring HATEOAS** is the library that implements this for Spring MVC/WebFlux. **HAL (Hypertext Application Language)** is a concrete, widely-used JSON convention for expressing hypermedia. A HAL document is just JSON with two reserved properties: - `_links`: an object whose keys are *link relations* (`self`, `next`, `orders`, …) and whose values contain at least an `href`. A relation may map to a single link object or an array of them. - `_embedded`: an object holding full representations of related resources inline (again keyed by relation), so a client can avoid extra round-trips. Everything else in the JSON is the resource's own state. The media type is `application/hal+json`, exposed in Spring as `MediaTypes.HAL_JSON`. **How you produce HAL in Spring HATEOAS.** You don't hand-write `_links`. You wrap your domain object in a *representation model*: - `EntityModel<T>` — a single domain object plus links. `EntityModel.of(employee, link1, link2)`. - `CollectionModel<T>` — a collection plus links. - `PagedModel<T>` — a page (adds a `page` metadata block with size/number/totalElements). Links are built type-safely with `WebMvcLinkBuilder`: `linkTo(methodOn(EmployeeController.class).one(id)).withSelfRel()` inspects the controller's `@RequestMapping`/`@GetMapping` to compute the URI, so refactoring a path updates the link automatically. `.withRel("orders")` names a custom relation; `.withSelfRel()` is shorthand for the `self` relation. **Enabling it.** With `spring-boot-starter-hateoas` on the classpath, Boot auto-configures a Jackson module and content negotiation for HAL — no explicit annotation needed. Without Boot you'd add `@EnableHypermediaSupport(type = HypermediaType.HAL)` to a config class. This registers the `Jackson2HalModule` that serializes `RepresentationModel` subclasses into the `_links`/`_embedded` shape. **Edge cases / gotchas.** - The link *relation* is the contract, not the URI. Clients should look up `_links.self.href`, never guess the path. - An empty `CollectionModel` still renders `_embedded` differently across versions; supply the element type (via `CollectionModel.empty()` fallbacks or an assembler) so the relation name is correct. - HAL is *read-oriented*: it tells clients where to go (GET) but not the shape of write requests. For that you need **HAL-FORMS** and the **Affordances API** (separate topics). - A plain `@RestController` returning a POJO produces `application/json` with no `_links`; you only get HAL when you return a `RepresentationModel` subtype and HAL support is enabled. **When to use.** HAL fits public or long-lived APIs where you want clients decoupled from URI structure and self-describing navigation. It adds ceremony, so trivial internal APIs often skip it.
- What is the difference between a link relation and an href, and why does it matter to clients?The relation (e.g. `self`, `orders`) is the stable, semantic key the client keys off; the href is the URI, which the server may change. Clients follow relations, so URI changes don't break them.
- What does `_embedded` give you that `_links` does not?`_embedded` inlines the full representation of related resources so the client avoids extra HTTP round-trips, whereas `_links` only points to where a related resource lives.
saying these in an interview costs you the question
- Claiming HAL requires you to manually write the `_links` JSON.
- Thinking a plain POJO returned from `@RestController` is automatically HAL.
- Saying clients should read the href to figure out routing instead of following relations.
- Confusing HAL (`application/hal+json`) with plain `application/json`.