Pagination
Relay provides flexible pagination support for iterating through API results with multiple strategies.
Pagination Strategies
Section titled “Pagination Strategies”Page-Based Pagination
Section titled “Page-Based Pagination”use Cline\Relay\Support\Attributes\Pagination\Pagination;
#[Get]#[Pagination( page: 'page', perPage: 'per_page', dataKey: 'data', totalPagesKey: 'meta.last_page', totalKey: 'meta.total',)]class GetUsersRequest extends Request{ public function endpoint(): string { return '/users'; }}Cursor-Based Pagination
Section titled “Cursor-Based Pagination”For APIs like Twitter, Stripe:
use Cline\Relay\Support\Attributes\Pagination\CursorPagination;
#[Get]#[CursorPagination( cursor: 'cursor', perPage: 'per_page', nextKey: 'meta.next_cursor', dataKey: 'data',)]class GetTimelineRequest extends Request{ public function endpoint(): string { return '/timeline'; }}Offset-Based Pagination
Section titled “Offset-Based Pagination”use Cline\Relay\Support\Attributes\Pagination\OffsetPagination;
#[Get]#[OffsetPagination( offset: 'offset', limit: 'limit', dataKey: 'data', totalKey: 'meta.total',)]class SearchRequest extends Request{ public function endpoint(): string { return '/search'; }}Link Header Pagination
Section titled “Link Header Pagination”For APIs following RFC 5988 (like GitHub):
use Cline\Relay\Support\Attributes\Pagination\LinkPagination;
#[Get]#[LinkPagination(dataKey: '')]class GetRepositoriesRequest extends Request{ public function endpoint(): string { return '/repos'; }}Using Paginated Responses
Section titled “Using Paginated Responses”Fetching Paginated Data
Section titled “Fetching Paginated Data”$connector = new GitHubConnector();$paginated = $connector->paginate(new GetUsersRequest());
// Get items from first page$users = $paginated->items();
// Check if more pages existif ($paginated->hasMore()) { // More pages available}Iterating All Pages
Section titled “Iterating All Pages”$paginated = $connector->paginate(new GetUsersRequest());
// Iterate through ALL items from ALL pagesforeach ($paginated as $user) { processUser($user);}Limiting Pages
Section titled “Limiting Pages”// Only fetch first 5 pages$paginated = $connector->paginate(new GetUsersRequest()) ->take(5);
foreach ($paginated as $user) { // Maximum 5 pages of users}Collecting All Items
Section titled “Collecting All Items”$allUsers = $connector->paginate(new GetUsersRequest()) ->collect();
$activeUsers = $allUsers->where('active', true);Memory-Efficient Iteration
Section titled “Memory-Efficient Iteration”$connector->paginate(new GetUsersRequest()) ->lazy() ->filter(fn ($user) => $user['active']) ->each(function ($user) { processUser($user); });Converting to Laravel Paginators
Section titled “Converting to Laravel Paginators”LengthAwarePaginator
Section titled “LengthAwarePaginator”$laravelPaginator = $paginated->toLaravelPaginator( perPage: 15, pageName: 'page', page: 1,);
return view('users.index', ['users' => $laravelPaginator]);SimplePaginator
Section titled “SimplePaginator”$simplePaginator = $paginated->toLaravelSimplePaginator( perPage: 15, pageName: 'page',);Custom Paginators
Section titled “Custom Paginators”Implement the Paginator contract for custom pagination strategies:
use Cline\Relay\Support\Contracts\Paginator;
class KeysetPaginator implements Paginator{ public function getNextPage(Response $response): ?array { $items = $this->getItems($response); if ($items === []) { return null; } $lastItem = end($items); return ['after' => $lastItem['id'] ?? null]; }
public function getItems(Response $response): array { return $response->json('data') ?? []; }
public function hasMorePages(Response $response): bool { return count($this->getItems($response)) >= 20; }}
// Usage$paginated = $connector->paginateWith( new GetItemsRequest(), new KeysetPaginator(),);Best Practices
Section titled “Best Practices”- Use appropriate strategy - Page/offset for stable data, cursor for real-time feeds
- Limit pages for large datasets - Use
->take(100)to prevent runaway pagination - Use lazy collections - Memory efficient for large datasets
- Handle empty pages - Check
$paginated->items() === []for no results