# Pagination

Watch the Pagination screencast
API Platform has native support for paged collections. Pagination is enabled by default for all
collections. Each collection contains 30 items per page. The activation of the pagination and the
number of elements per page can be configured from:
- the server-side (globally or per resource)
- the client-side, via a custom GET parameter (disabled by default)
When issuing a `GET` request on a collection containing more than 1 page (here `/books`), a
[Hydra collection](https://www.hydra-cg.com/spec/latest/core/#collections) is returned. It's a valid
JSON(-LD) document containing items of the requested page and metadata.
```json
{
"@context": "/contexts/Book",
"@id": "/books",
"@type": "Collection",
"member": [
{
"@id": "/books/1",
"@type": "https://schema.org/Book",
"name": "My awesome book"
},
{
"_": "Other items in the collection..."
}
],
"totalItems": 50,
"view": {
"@id": "/books?page=1",
"@type": "PartialCollectionView",
"first": "/books?page=1",
"last": "/books?page=2",
"next": "/books?page=2"
}
}
```
Hypermedia links to the first, the last, previous and the next page in the collection are displayed
as well as the number of total items in the collection.
The name of the page parameter can be changed with the following configuration:
## Changing page parameter name with Symfony
```yaml
# api/config/packages/api_platform.yaml
api_platform:
collection:
pagination:
page_parameter_name: _page
```
## Changing page parameter name with Laravel
```php
[
'pagination' => [
'page_parameter_name' => '_page',
]
],
];
```
## Disabling the Pagination
Paginating collections is generally accepted as a good practice. It allows browsing large
collections without too much overhead as well as preventing
[DOS attacks](https://en.wikipedia.org/wiki/Denial-of-service_attack). However, for small
collections, it can be convenient to fully disable the pagination.
### Disabling the Pagination Globally
#### Disabling the Pagination Globally with Symfony
The pagination can be disabled for all resources using this configuration:
```yaml
# api/config/packages/api_platform.yaml
api_platform:
defaults:
pagination_enabled: false
```
#### Disabling the Pagination Globally with Laravel
The pagination can be disabled for all resources using this configuration:
```php
[
'pagination_enabled' => false,
],
];
```
### Disabling the Pagination For a Specific Resource
It can also be disabled for a specific resource:
```php
### Disabling the Pagination For a Specific Operation
You can also disable an operation for a specific operation:
```php
```
### Allowing the Client to Control Pagination
By default, clients cannot enable or disable pagination via query parameters
(`pagination_client_enabled` defaults to `false`). You can allow clients to control pagination by
setting this option to `true`.
#### Allowing the Client to Control Pagination Globally
##### Allowing the Client to Control Pagination Globally with Symfony
To enable this feature globally, use the following configuration:
```yaml
# api/config/packages/api_platform.yaml
api_platform:
defaults:
pagination_client_enabled: true
collection:
pagination:
enabled_parameter_name: pagination # optional
```
The pagination can now be enabled or disabled by adding a query parameter named `pagination`:
- `GET /books?pagination=false`: disabled
- `GET /books?pagination=true`: enabled
Any value accepted by the
[`FILTER_VALIDATE_BOOLEAN`](https://www.php.net/manual/en/filter.filters.validate.php) filter can be
used as the value.
##### Allowing the Client to Control Pagination Globally with Laravel
To enable this feature globally, use the following configuration:
```php
[
'pagination_client_enabled' => true,
],
'collection' => [
'pagination' => [
'enabled_parameter_name' => 'pagination', // optional
],
],
];
```
The pagination can now be enabled or disabled by adding a query parameter named `pagination`:
- `GET /books?pagination=false`: disabled
- `GET /books?pagination=true`: enabled
Any value accepted by the
[`FILTER_VALIDATE_BOOLEAN`](https://www.php.net/manual/en/filter.filters.validate.php) filter can be
used as the value.
#### Allowing the Client to Control Pagination For a Specific Resource
The client ability to control pagination can also be enabled for a specific resource:
```php
[
'pagination_items_per_page' => 30,
],
];
```
### Changing the Number of Items per Page For a Specific Resource
```php
[
'pagination_client_items_per_page' => true,
],
'collection' => [
'pagination' => [
'items_per_page_parameter_name' => 'itemsPerPage',
],
],
];
```
The number of items per page can now be changed adding a query parameter named `itemsPerPage`:
`GET /books?itemsPerPage=20`.
#### Changing the Number of Items per Page Client-side For a Specific Resource
Changing the number of items per page can be enabled (or disabled) for a specific resource:
```php
[
'pagination_maximum_items_per_page' => 50,
],
];
```
### Changing Maximum Items Per Page For a Specific Resource
```php
[
'pagination_partial' => true, // Disabled by default
],
];
```
### Partial Pagination For a Specific Resource
```php
[
'pagination_client_partial' => true, // Disabled by default
],
'collection' => [
'pagination' => [
'partial_parameter_name' => 'partial' // Default value
],
],
];
```
The partial pagination retrieval can now be changed by toggling a query parameter named `partial`:
`GET /books?partial=true`.
#### Partial Pagination Client-side For a Specific Resource
```php
'id', 'direction' => 'DESC']
]
)]
#[ApiFilter(RangeFilter::class, properties: ["id"])]
#[ApiFilter(OrderFilter::class, properties: ["id" => "DESC"])]
class Book
{
// ...
}
```
To know more about cursor-based pagination take a look at
[this blog post on medium (draft)](https://medium.com/@sroze/74fd1d324723).
## Pagination for Custom State Providers
If you are using custom state providers (not the provided Doctrine ORM, Doctrine ODM, ElasticSearch
or Laravel Eloquent ones) and if you want your results to be paginated, you will need to return an
instance of a `ApiPlatform\State\Pagination\PartialPaginatorInterface` or
`ApiPlatform\State\Pagination\PaginatorInterface`. A few existing classes are provided to make it
easier to paginate the results:
- `ApiPlatform\State\Pagination\ArrayPaginator`
- `ApiPlatform\State\Pagination\TraversablePaginator`
## Controlling The Behavior of The Doctrine ORM Paginator
The
[PaginationExtension](https://github.com/api-platform/core/blob/main/src/Doctrine/Orm/Extension/PaginationExtension.php)
of API Platform performs some checks on the `QueryBuilder` to guess, in most common cases, the
correct values to use when configuring the Doctrine ORM Paginator:
- `$fetchJoinCollection` argument: Whether there is a join to a collection-valued association. When
set to `true`, the Doctrine ORM Paginator will perform an additional query, in order to get the
correct number of results.
You can configure this using the `paginationFetchJoinCollection` attribute on a resource or on a
per-operation basis:
```php
tokenStorage = $tokenStorage;
}
public function getBooksByFavoriteAuthor(int $page = 1): Paginator
{
$firstResult = ($page -1) * self::ITEMS_PER_PAGE;
$user = $this->tokenStorage->getToken()->getUser();
$queryBuilder = $this->createQueryBuilder();
$queryBuilder->select('b')
->from(Book::class, 'b')
->where('b.author = :author')
->setParameter('author', $user->getFavoriteAuthor()->getId())
->andWhere('b.publicatedOn IS NOT NULL');
$criteria = Criteria::create()
->setFirstResult($firstResult)
->setMaxResults(self::ITEMS_PER_PAGE);
$queryBuilder->addCriteria($criteria);
$doctrinePaginator = new DoctrinePaginator($queryBuilder);
$paginator = new Paginator($doctrinePaginator);
return $paginator;
}
}
```
The Controller would look like this:
```php
query->get('page', 1);
return $bookRepository->getBooksByFavoriteAuthor($page);
}
}
```
The service needs to use the proper repository method. You can also use the Query object inside the
repository method and pass it to the Paginator instead of passing the QueryBuilder and using
Criteria. Second Example:
```php
tokenStorage->getToken()->getUser();
$queryBuilder = $this->createQueryBuilder();
$queryBuilder->select('b')
->from(Book::class, 'b')
->where('b.author = :author')
->setParameter('author', $user->getFavoriteAuthor()->getId())
->andWhere('b.publicatedOn IS NOT NULL');
$query = $queryBuilder->getQuery()
->setFirstResult($firstResult)
->setMaxResults(self::ITEMS_PER_PAGE);
$doctrinePaginator = new DoctrinePaginator($query);
$paginator = new Paginator($doctrinePaginator);
return $paginator;
}
}
```