Paginator, validates them against the strategy you chose, and emits a standard pagination envelope on the response.
1. Choose a strategy
setMaxLimit() caps what a client can request; the default max is 100. RequestValidationMiddleware rejects limit values above that with 422.
When to pick which
| Strategy | Good for | Gotchas |
|---|---|---|
| Page | Admin UIs, “show me page 3 of results” | Skewed results when underlying data mutates mid-browse |
| Offset | Same as page, but when you want explicit skip semantics | Large offsets are expensive in SQL |
| Cursor | High-throughput feeds, stable paging over mutating data | Clients must respect the opaque cursor — can’t jump to “page 5” |
2. Read the paginator in the controller
$request->paginator() returns a PagePaginator, OffsetPaginator, or CursorPaginator depending on the route’s strategy.
Page
Offset
Cursor
3. The JSON envelope
setPaginationResponse() merges a pagination key into the response next to data:
limit / offset / total for offset, current_cursor / next_cursor / has_more for cursor.
Inside a resource
AbstractListResourceController::pagination() is the hook:
ResourceListResponse, which takes the PaginationResponseInterface directly in its constructor. See the resource CRUD how-to.
OpenAPI side effects
Apivalk injects the relevant query parameters (page, limit, offset, cursor) into the generated spec, typed and documented, with the setMaxLimit constraint reflected. The response schema gets the correct pagination envelope.