Javacodegeeks iconJavacodegeeksSep 29, 2026 ~7 min source read

HATEOAS Pagination: Why PagedModel Stops Working When You Use Custom Repositories

Spring Data REST and Spring HATEOAS build pagination links automatically for standard repositories because they control the request lifecycle. Custom repository implementations break that chain, so you must assemble pagination links yourself and take care of total-count queries.

HATEOAS Pagination: Why PagedModel Doesn’t Work with Custom Repositories

Share this story

Send the public story page.

Useful takeaways from this story.

You're left writing pagination metadata by hand, wondering why a framework that promised convention over configuration abandoned you the moment you needed control.

It's a structural consequence of how Spring Data REST and Spring HATEOAS actually generate those links, and understanding why will save you hours of debugging a "missing feature" that was never going to...

Spring Data REST doesn't generate those links through some clever runtime inspection of your database.

# What breaks Spring Data REST exposes PagingAndSortingRepository endpoints with automatic HATEOAS pagination: PagedModel responses that include next, prev, first, last links and page metadata. That works because the framework owns the full request-to-response flow: it receives pageable parameters, runs the derived query, creates a Page, and hands everything to PagedResourcesAssembler which knows the current URI and request context to generate correct links.

# The concrete failure modes

  • Missing navigation links: responses contain raw Page content serialized by Jackson, with no next/prev/self links.
  • Incorrect last page: many custom queries require a separate COUNT query. If that count query is omitted or inconsistent with the filtering in the main query, totalElements will be wrong and last-page links (when assembled) will point to incorrect pages.

# Why the framework behaves this way

# Practical example Imagine a ProductRepositoryCustom that implements a geo-radius search which no derived method can express:

The implementation runs a native query, maps results, and returns new PageImpl (content, pageable, total). Functionally the paging results are correct, but Spring Data REST won't automatically assemble next/prev links. To produce HATEOAS output you must assemble it at the controller layer.

# What to do instead

  • Keep Pageable in your method signature. Return a Page so callers and controllers can access paging metadata.
  • Provide an explicit COUNT query when using native SQL or custom criteria queries. Ensure it mirrors all filters and joins used in the main query so totalElements is accurate.
  • Expose the custom method via a @RepositoryRestController or @RestController where the assembler and request context are available. Don't expect Spring Data REST's automatic repository exposure to handle custom implementation wiring for you.

# Quick checklist for custom-repository pagination

  • Method accepts Pageable and returns Page.
  • Controller injects PagedResourcesAssembler and calls toModel(page).
  • Manual COUNT query provided and validated against main query filters.
  • Verify generated URIs include the expected query parameters (page, size, sort).

# Final note

More context around this story.

Spring Boot CRUD Operations with Supabase
Javacodegeeks iconJavacodegeeksSep 22, 2026

Spring Boot CRUD Operations with Supabase

Spring Boot is widely used for building REST APIs and backend applications in Java. When an application needs persistent storage, Spring Boot can connect to traditional databases such as PostgreSQL, MySQL, or Oracle. Supabase provides another convenient option because it offers a managed PostgreSQL database along with

Loading more related stories...

Keep reading in the app

Open the app view to save this story, compare related coverage, and continue from the same source.

Open in app