Skip to content

Using filter in query parameters breaks pagination #7888

@elfin-sbreuers

Description

@elfin-sbreuers

API Platform version(s) affected: at least 4.2 and 4.3

Description
When using filters via a nested filter[...] query parameter like:

http://<domain>/api/endpoint?filter[custom]=true

the default pagination parameters stop working as expected:

  • page
  • itemsPerPage
  • pagination

After investigation, it appears that API Platform then expects pagination parameters to also be nested under filter, e.g.:

http://<domain>/api/endpoint?filter[custom]=true&filter[pagination]=false

If no filter[...] parameter is used, pagination works as documented:

http://<domain>/api/endpoint?page=2&itemsPerPage=10

Additionally, the pagination links returned in the response suggest that pagination parameters are still expected at the top level:

"links": {
  "self": "/api/endpoint?filter[custom]=true&reload=true&page=1",
  "first": "/api/endpoint?filter[custom]=true&reload=true&page=1",
  "last": "/api/endpoint?filter[custom]=true&reload=true&page=7",
  "next": "/api/endpoint?filter[custom]=true&reload=true&page=2"
}

This creates an inconsistency between how the API expects parameters and how it advertises them.

Possible Solution
Pagination handling should be independent of how filters are structured. Ideally, pagination parameters (page, itemsPerPage, pagination) should always be accepted at the top level, regardless of whether filters are passed as:

  • flat query parameters (?custom=true), or
  • nested (?filter[custom]=true)

This would make the behavior more predictable and avoid coupling pagination parsing to filter structure.

Additional Context
The root cause appears to be how $context['filters'] is populated.

When using flat query parameters, pagination values (page, itemsPerPage) are present in $context['filters'] and correctly processed by ApiPlatform\State\Pagination\Pagination.
When using filter[...], only the nested filter values are included in $context['filters'], while pagination parameters remain outside and are therefore ignored by the pagination logic.

This leads to pagination not being applied unless it is also nested under filter[...].

Another source of confusion is the expectation based on the JSON:API specification:

https://jsonapi.org/format/#fetching-pagination
https://jsonapi.org/format/#fetching-filtering
https://jsonapi.org/format/#query-parameters-families

According to the JSON:API spec, query parameter families like filter[...] and page[...] are meant to coexist. Coming from that background, it is natural to structure requests like:

?filter[custom]=true&page[number]=2&page[size]=10

However, API Platform does not follow this convention and instead mixes pagination parameters into the same namespace (filters) internally. This mismatch leads to unexpected behavior when trying to combine structured filtering with pagination.

Clarifying this behavior in the documentation—or aligning parameter handling more closely with JSON:API conventions—would help avoid confusion.

Even more desireable would be to support the JSON:API spec for filtering and pagination when jsonapi is used as content type.

I would try to provide a solution, but I was lost when searching for where the significant portion of the $context variable population is happening.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions