Spring HATEOAS
_embedded property
Spring REST
Spring framework
API development

How to remove the _embedded property in Spring HATEOAS

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Spring HATEOAS, _embedded is the standard HAL field for collection resources, so it appears whenever you return CollectionModel with HAL media type. If you want to remove _embedded, you are changing the response format, not toggling a cosmetic option. The fix is to return a non HAL payload or create a custom wrapper that exposes items under your own field name.

Why _embedded Appears

HAL defines collection structure with two key sections:

  • _embedded for resource arrays.
  • _links for navigational links.

Spring HATEOAS follows this spec when your endpoint produces application/hal+json. For example, returning CollectionModel<EntityModel<UserDto>> will generate _embedded by design.

Option 1: Return Plain JSON Without HAL

If your consumers do not need HAL semantics, return a plain DTO list or wrapper and produce application/json.

java
1public record UserDto(Long id, String name) {}
2
3public record UsersResponse(List<UserDto> items, int count) {}
4
5@RestController
6@RequestMapping("/users")
7public class UserController {
8
9    @GetMapping(produces = "application/json")
10    public UsersResponse listUsers() {
11        var items = List.of(
12            new UserDto(1L, "Ava"),
13            new UserDto(2L, "Noah")
14        );
15        return new UsersResponse(items, items.size());
16    }
17}

This removes _embedded entirely because you are no longer using HAL collection serialization.

Some teams want links but not HAL naming. In that case, avoid CollectionModel and define your own response object that includes items plus link fields.

java
1import org.springframework.hateoas.Link;
2
3public record UsersWithLinksResponse(
4        List<UserDto> items,
5        List<Link> links
6) {}
7
8@RestController
9@RequestMapping("/users")
10public class UserLinkController {
11
12    @GetMapping(produces = "application/json")
13    public UsersWithLinksResponse listUsers() {
14        var users = List.of(
15            new UserDto(1L, "Ava"),
16            new UserDto(2L, "Noah")
17        );
18
19        var links = List.of(
20            Link.of("/users", "self"),
21            Link.of("/users?page=2", "next")
22        );
23
24        return new UsersWithLinksResponse(users, links);
25    }
26}

This keeps discoverability while avoiding HAL reserved keys.

Option 3: Serve HAL and JSON on Different Endpoints

If some clients need HAL and others do not, expose content negotiation explicitly.

java
1@GetMapping(produces = "application/hal+json")
2public CollectionModel<EntityModel<UserDto>> listUsersHal() {
3    var user1 = EntityModel.of(new UserDto(1L, "Ava"));
4    var user2 = EntityModel.of(new UserDto(2L, "Noah"));
5    return CollectionModel.of(List.of(user1, user2));
6}
7
8@GetMapping(path = "/flat", produces = "application/json")
9public UsersResponse listUsersFlat() {
10    var items = List.of(new UserDto(1L, "Ava"), new UserDto(2L, "Noah"));
11    return new UsersResponse(items, items.size());
12}

This is often the least disruptive migration path.

What Not to Do

Do not attempt random Jackson field filters directly on HAL output unless you fully own all clients and serializers. Removing _embedded from HAL while keeping the rest of HAL can break clients that expect spec compliant responses. If you must do custom serialization, treat it as a new media type and version it.

Testing Strategy

Add endpoint tests for both payload shape and content type. Verify:

  • application/json response contains items, not _embedded.
  • HAL endpoint still includes _embedded for compatibility.
  • Link names and URLs remain stable across refactors.
java
1@WebMvcTest
2class UserControllerTest {
3
4    @Autowired MockMvc mvc;
5
6    @Test
7    void flatResponseDoesNotContainEmbedded() throws Exception {
8        mvc.perform(get("/users").accept("application/json"))
9           .andExpect(status().isOk())
10           .andExpect(jsonPath("$._embedded").doesNotExist())
11           .andExpect(jsonPath("$.items[0].name").value("Ava"));
12    }
13}

Migration Checklist

Before releasing a non HAL payload, confirm client expectations, API documentation, and contract tests are updated together. If external consumers depend on HAL, introduce a versioned endpoint instead of changing existing responses in place. This keeps integrations stable while you transition clients gradually.

Common Pitfalls

  • Expecting _embedded removal while still returning HAL CollectionModel.
  • Mixing HAL and non HAL responses on the same endpoint without explicit media type handling.
  • Changing payload shape without versioning or consumer communication.
  • Returning links as raw strings with inconsistent relation names.
  • Testing only status code and not serialized JSON shape.

Summary

  • _embedded is part of HAL collection format, not a random Spring field.
  • Remove it by switching to non HAL JSON or custom response wrappers.
  • Keep HAL endpoints when backward compatibility is required.
  • Use explicit content types so clients know what format to expect.
  • Add shape focused tests to prevent accidental response regressions.

Course illustration
Course illustration

All Rights Reserved.