You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Pagination the REST way : 'Content-Range' header #7137
For example if you have a dictionary API and you already have a 'count' word definition, it's unconvenient to add a api.foo/words/count route. It's also not very RESTful to create and impose the usage of a new verb COUNT, dedicated for this operation.
What we want, ideally, is to have the number of items in the pagination of this current request, ('30 items' in your book demo API), how many resources can be found on every page ('100 items' in total?), and therefor you can calculate how much pages there is. Also, for economic purpose, we want the content of the response to be empty or totally different of the other call on this resource, for example with just those counts.
And I feel like there is a very nice way to deal with this: as a user, you can request your book/ resource with the HEAD verb, that will ensure no body in response, and as an API provider, you can enrich your response with this nice 'Content-Range' header!
HEAD verb
HEAD works like... HEAD, it's already handled, so nothing to say more.
Content-Range HTTP header
'Content-Range' header is intended to carry a bunch of very clever information, as the common example is:
Content-Range: bytes 200-1000/67589
Where we can find the unit, the current range, and / the total count. Implicitely, you can find the number of items served in this response, here 1000-200 = 800 bytes, the current page, and the total number of pages. The tricky part is that the <unit> is usually "bytes", but the specifications doesn't limit!
The <unit> is the resource on plural, an information API-Platform already has when naming routes,
The <range-start>-<range-end> is defined by pagination,
The <size> is the total count of this request.
I feel like there is some MASSIVE advantages in it:
very convenient info about current pagination, even in regular JSON answers (as hydra returns a hydra:totalItems key for it),
clever usage of HEAD verb for economic requests as you don't need to charge all the jsonLD payload to have info,
native HTTP solution that totally adheres in REST,
saves few headaches of 'how will provide a count for this request on my API?' with an out of the box solution; JSON+LD advantages for everyone!
You can argue that if specifications don't limit unit to be bytes, they don't limit verbs also; The point is that adding a custom verb like COUNT force requesters to know it, firewalls to accept it. On the other hand, Content-Range header usage with a 'resource' as unit is a flat win for requester, doesn't break current API and won't break anything in any system dealing with the answer.
I feel like it's enough for a first implementation (I may be very wrong, but as json+ld implementation already has those info, it's more like populating this header), but in future a nice improvement is Range: header can also be used instead of a page query parameter, to have a complete pagination handling in headers. It allows also to give some love to the Accept-Ranges: header and maybe the OPTION verb to document how to deal with pagination (ranges is the correct name in fact)!
When returning a full list of resources (for example 3 books in database, so a GET request will get the fullset) --> code 200 OK
When returning a partial list of resources (GET request on an API where the number of items > number of items per age) --> Code 206 Partial Content
When being requested out of range (request's Range header, but also applie on a classic request with pagination for example /books?page=999 if I have only 20 books) --> Code 416 Range Not Satisfiable
Not sure why you'd need this let's use only the operation's name or something else (maybe that we can discuss this later on)
The name of the range unit is needed for the HTTP header 'Content-Range' on response, as defined here. For example, if I call the bookshop demo API with this url https://demo.api-platform.com/books?page=2 then the response should contain a Content-Range: books 31-60/201
So I don't know if there is a better way to get the resource name, put it in plural, then in lowercase, than the getShortName() method of Metadata class?
yes but only if the user didn't specify its own status
Status code seems a bit more complicated or subtle to use it accordingly with RFC 9110; 206 should be sent only if client asks for a range that can't be responded as it:
if client ask for the whole books collection, and the server has a limit of 30 books per response, then a 206 should be fired,
if client ask for a given page, then server should fire a 200,
if client, in any given way, ask for books 31-65 (so 35 books), then the server will fire 30 books and a 206.
So implementing this is can be cut in subtasks:
tweaking the server response (status code, Content-Range, Accept-Ranges), independently,
tweaking the server's controller to catch new headers and take decision with other params & controls (for example response format can be for now either controlled with Accept header or in URL; That should be the same for pagination/range control) independently.
Love the proposal, but is using a specific resource type as the unit a good idea instead of something generic like record, item, or result? Clients need to know how to interpret a given unit, so every application having heaps of units that are practically all handled the same way seems like a suboptimal way to handle generic record pagination.
There is actually a HTTP Range Unit Registry maintained by IANA that currently only holds bytes and none, so it would be amazing to have a standard unit added to the registry here that other projects in the broader web API ecosystem could use as well.
@Radiergummi you are right, there's a subject here as "unit" is arbitrary; Standard allows to use something else than "bytes", but the limit is up to us.
having "items" as a unit is not really helpful as all our routes will return this unit,
API-Platform has a built-in system to pluralize names so it's convenient,
we normally knows which "kind of items" we retrieve by reading the route,
In fact as I'm writing I found 100 reasons for and 100 reasons against. So I don't know and let you argue around this!
My main argument would be that the concept of a unit is semantically separate from the concrete thing it is measuring: A litre is a litre, regardless of whether you're looking at a glass of water or a pint of beer. I know how many litres my pitcher can hold, but I've got no clue how many books that is.
In this concrete case, to a client that isn't necessarily the final consumer of the responses (say, a cache or a proxy or even just curl or Postman), having a single unit to handle limits the complexity a lot, and even enables higher-level integration: If there was an IANA unit entry, I'm pretty sure you could convince e.g. the httpie maintainers to add automatic pagination, for example.
A single unit as a way to measure things allows to have well-known and shared semantics on how to handle it correctly, regardless of what kinds of things you have; that isn't really relevant to pagination itself anyway, really. When I request a list of books, "items" clearly refers to the books in the response.
Edit: To expand on the above a little, I think there are also practical concerns. As a user, I should be able to implement generic pagination handling without having to know what kind of resources I am dealing with.
Activity
Nayte91 commentedon May 8, 2025
In API-Platform/core:
soyuka commentedon May 9, 2025
Not sure why you'd need this let's use only the operation's name or something else (maybe that we can discuss this later on)
https://github.com/api-platform/core/blob/c08758c93346b5819ab924ba1e7fe144c74f0007/src/State/Provider/ContentNegotiationProvider.php
Use a PartialPaginator for this?
yes but only if the user didn't specify its own status
Nayte91 commentedon May 9, 2025
The name of the range unit is needed for the HTTP header 'Content-Range' on response, as defined here. For example, if I call the bookshop demo API with this url https://demo.api-platform.com/books?page=2 then the response should contain a
Content-Range: books 31-60/201So I don't know if there is a better way to get the resource name, put it in plural, then in lowercase, than the getShortName() method of Metadata class?
Status code seems a bit more complicated or subtle to use it accordingly with RFC 9110; 206 should be sent only if client asks for a range that can't be responded as it:
So implementing this is can be cut in subtasks:
soyuka commentedon May 13, 2025
👍 let me know if you need help
Radiergummi commentedon Apr 1, 2026
Love the proposal, but is using a specific resource type as the unit a good idea instead of something generic like record, item, or result? Clients need to know how to interpret a given unit, so every application having heaps of units that are practically all handled the same way seems like a suboptimal way to handle generic record pagination.
There is actually a HTTP Range Unit Registry maintained by IANA that currently only holds
bytesandnone, so it would be amazing to have a standard unit added to the registry here that other projects in the broader web API ecosystem could use as well.Nayte91 commentedon Apr 1, 2026
@Radiergummi you are right, there's a subject here as "unit" is arbitrary; Standard allows to use something else than "bytes", but the limit is up to us.
In fact as I'm writing I found 100 reasons for and 100 reasons against. So I don't know and let you argue around this!
Radiergummi commentedon Apr 1, 2026
My main argument would be that the concept of a unit is semantically separate from the concrete thing it is measuring: A litre is a litre, regardless of whether you're looking at a glass of water or a pint of beer. I know how many litres my pitcher can hold, but I've got no clue how many books that is.
In this concrete case, to a client that isn't necessarily the final consumer of the responses (say, a cache or a proxy or even just curl or Postman), having a single unit to handle limits the complexity a lot, and even enables higher-level integration: If there was an IANA unit entry, I'm pretty sure you could convince e.g. the httpie maintainers to add automatic pagination, for example.
A single unit as a way to measure things allows to have well-known and shared semantics on how to handle it correctly, regardless of what kinds of things you have; that isn't really relevant to pagination itself anyway, really. When I request a list of books, "items" clearly refers to the books in the response.
Edit: To expand on the above a little, I think there are also practical concerns. As a user, I should be able to implement generic pagination handling without having to know what kind of resources I am dealing with.