> For the complete documentation index, see [llms.txt](https://docs.opensoutheners.com/oss/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opensoutheners.com/oss/laravel-apiable/responses/pagination.md).

# Pagination

Configure pagination strategies for your JSON:API responses including length-aware, simple, and cursor-based pagination.

All `JsonApiResponse` list responses are paginated by default. The package wraps results in a `JsonApiCollection` that produces JSON:API-compliant `links` and `meta` objects.

## Query parameter format

Clients control pagination using dot-bracket notation:

```
GET /api/films?page[number]=2&page[size]=25
```

| Parameter      | Description                                                                                                                                       | Default                                  |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- |
| `page[number]` | Page number (1-based). Used by the length-aware and simple strategies.                                                                            | `1`                                      |
| `page[size]`   | Items per page                                                                                                                                    | `responses.pagination.default_size` (50) |
| `page[cursor]` | Opaque cursor value. Used by the cursor strategy instead of `page[number]`; taken from the `links.next`/`links.prev` URLs of a previous response. | —                                        |

## Pagination strategies

Three strategies are available, controlled by `config/apiable.php` or overridden per response.

### Length-aware (default)

Executes a `COUNT` query before fetching results. Returns full pagination metadata including the total item count, last page number, and page links.

```php
// config/apiable.php
'responses' => [
    'pagination' => [
        'default_size' => 50,
    ],
],
```

This is the default behavior — no extra configuration required.

### Simple

No `COUNT` query. Only knows whether a next or previous page exists. Useful when counting all rows is expensive and the total is not needed by the client. `meta.total`, `meta.last_page`, and `links.last` are omitted from the response.

```php
JsonApiResponse::from(Film::class)->simplePaginating();

// With a custom page size, same as passing `page[size]`
JsonApiResponse::from(Film::class)->simplePaginating(pageSize: 20);
```

Or set it globally:

```php
// config/apiable.php — set 'type' key (add it if missing)
'pagination' => [
    'type' => 'simple',
    'default_size' => 50,
],
```

### Cursor

Cursor-based pagination for large datasets. Avoids `OFFSET` queries entirely, making it efficient for deep pages. The client receives an opaque cursor via the `page[cursor]` query parameter instead of a page number — read it off the `links.next`/`links.prev` URLs of the previous response rather than constructing it manually. `meta.total`, `meta.current_page`, `links.first`, and `links.last` are omitted, since a cursor has no concept of page position or total count.

```php
JsonApiResponse::from(Film::class)->cursorPaginating();

// With a custom page size
JsonApiResponse::from(Film::class)->cursorPaginating(pageSize: 20);
```

Or set it globally:

```php
// config/apiable.php
'pagination' => [
    'type' => 'cursor',
    'default_size' => 50,
],
```

{% hint style="warning" %}
Cursor pagination requires the query to be sorted by a unique, sequential column (e.g. `id` or `created_at`). Without a deterministic sort order the cursor position is undefined.
{% endhint %}

## FastPaginate integration

When the [hammerstone/fast-paginate](https://github.com/hammerstonedev/fast-paginate) package is installed it is automatically detected and used inside `jsonApiPaginate`. No configuration is required — the package checks for both the `Hammerstone\FastPaginate\FastPaginate` and `AaronFrancis\FastPaginate\FastPaginate` class names at runtime.

```bash
composer require hammerstone/fast-paginate
```

After installation all length-aware paginated responses will use `fastPaginate()` instead of the standard paginator, which avoids expensive `COUNT` queries on large tables by using a subquery approach.

## The jsonApiPaginate builder macro

The package registers a `jsonApiPaginate` macro on `Illuminate\Database\Eloquent\Builder`. You can call it directly on any Eloquent builder if you need to paginate outside of `JsonApiResponse`:

```php
$collection = Film::where('active', true)->jsonApiPaginate();

// With explicit page size
$collection = Film::where('active', true)->jsonApiPaginate(pageSize: 10);

// Selecting specific columns
$collection = Film::jsonApiPaginate(columns: ['id', 'title', 'created_at']);
```

**Signature:**

```php
jsonApiPaginate(
    null|int|string $pageSize = null,
    array $columns = ['*'],
    string $pageName = 'page.number',
    ?int $page = null,
): JsonApiCollection
```

## Custom pagination logic

Use `paginateUsing()` on `JsonApiResponse` to replace the pagination mechanism entirely with your own closure:

```php
JsonApiResponse::from(Film::class)
    ->paginateUsing(function ($query) {
        return $query->simplePaginate(request()->integer('per_page', 20));
    });
```

The closure receives the Eloquent builder (or model instance) after all pipeline stages (filters, sorts, includes, fields) have been applied.

## Example JSON response

A length-aware paginated response looks like this:

```json
{
  "data": [
    {
      "id": "1",
      "type": "film",
      "attributes": {
        "title": "The Lost City",
        "created_at": "2021-07-21T22:23:39.000000Z"
      }
    }
  ],
  "links": {
    "first": "http://localhost:8000/api/films?page%5Bnumber%5D=1",
    "last": "http://localhost:8000/api/films?page%5Bnumber%5D=4",
    "prev": null,
    "next": "http://localhost:8000/api/films?page%5Bnumber%5D=2"
  },
  "meta": {
    "current_page": 1,
    "from": 1,
    "last_page": 4,
    "links": [
      { "url": null, "label": "&laquo; Previous", "active": false },
      { "url": "http://localhost:8000/api/films?page%5Bnumber%5D=1", "label": "1", "active": true },
      { "url": "http://localhost:8000/api/films?page%5Bnumber%5D=2", "label": "2", "active": false },
      { "url": null, "label": "Next &raquo;", "active": false }
    ],
    "path": "http://localhost:8000/api/films",
    "per_page": 50,
    "to": 50,
    "total": 183
  }
}
```

{% hint style="info" %}
The `links` URLs use percent-encoded bracket notation (`page%5Bnumber%5D`) which is equivalent to `page[number]`. Most HTTP clients decode these automatically.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.opensoutheners.com/oss/laravel-apiable/responses/pagination.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
