Paging a REST Endpoint

Pageable as a Controller Parameter

Spring Data web support binds ?page=&size=&sort= query parameters to a Pageable automatically. @PageableDefault sets defaults.

@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductRepository products;

    @GetMapping
    public PagedModel<ProductCardDto> list(
            @RequestParam String category,
            @PageableDefault(size = 20, sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
        return new PagedModel<>(products.findByCategoryName(category, pageable).map(ProductCardDto::from));
    }
}

Configuration

Limit page size globally so nobody can ask for size=100000:

application.properties

spring.data.web.pageable.max-page-size=100
spring.data.web.pageable.default-page-size=20

Request and Response

Example

curl "localhost:8080/api/products?category=Books&page=1&size=3&sort=price,asc"

Example output

HTTP/1.1 200 OK
Content-Type: application/json

{
  "content": [
    {"id": 64, "name": "Atomic Habits", "price": 399.00},
    {"id": 71, "name": "The Psychology of Money", "price": 349.00},
    {"id": 52, "name": "Wings of Fire", "price": 299.00}
  ],
  "page": {"size": 3, "number": 1, "totalElements": 41, "totalPages": 14}
}

Note: Returning a raw PageImpl from a controller makes Spring Data 3.3+ log a warning, because its JSON shape is not a stable contract. Wrap it in PagedModel (as above) or enable @EnableSpringDataWebSupport(pageSerializationMode = VIA_DTO).

Practice

Exercise: Add an endpoint GET /api/me/orders that returns the current user's orders as a Slice, newest first, 10 per page by default.

Show answer
// repository
Slice<OrderSummaryView> findByUserId(Long userId, Pageable pageable);

// controller
@GetMapping("/api/me/orders")
public Slice<OrderSummaryView> myOrders(@AuthenticationPrincipal ShopUser me,
        @PageableDefault(size = 10, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable) {
    return orderRepository.findByUserId(me.getId(), pageable);
}