2

How should a RESTful API handle pagination in the situation where a client may want the ability to jump to arbitrary pages? Let's assume we are using the Link header in the way the GitHub API does:

Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next", <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"

The API, like many others, returns links to the next page, previous page, first page and last page. This does not account for the relatively common use-case of providing on the client-side links to a range of pages, and not just the next/previous/first/last. If, for example, the request returns 100 pages worth of results, the client may want to allow the ability to jump to an arbitrary page. How should this be done while adhering closely to the concepts of REST?

Some possible options:

  • Just let the client infer the other links. You have the first and last page, it may be a reasonable assumption to modify the link to obtain the other pages. This seems to go against HATEOAS (which is always the principle of REST that seems to cause problems).
  • Return a link to every page. This seems unwieldy as the potential number of pages could be high.
  • Return a link to a small range of pages (perhaps 2 either side of the current page). This seems like a relatively sensible approach but it limits the options clients have when it comes to displaying pagination information.
  • 3
    What's the problem with the examples you just provided? – Robert Harvey May 5 '15 at 20:00
  • @RobertHarvey - The problem with the GitHub and JSONAPI examples is that they only allow navigation to the first/last/next/previous pages. If you have 100 pages the client may want to be able to jump to some arbitrary page. – James Allardice May 5 '15 at 20:07
  • @RobertHarvey - I've edited the question so I hope it significantly clearer now. – James Allardice May 6 '15 at 19:15
  • Well, according to the example you posted above, you can jump to any page of n pages given x items per page: ?page=3&per_page=100. So I'd say you have the ability to jump to anywhere you want. – Robert Harvey May 6 '15 at 19:26
  • 1
    No, HATEOAS is an additional constraint, on top of REST. Check the Wikipedia entry. All it really means is that you don't provide links to non-existent resources or actions. – Robert Harvey May 6 '15 at 19:47
3

The problem is the URL is not RESTful in the GitHub example:

I would rather see actual URL that represent the resource exclusively. It is not that hard if you put a little thought into it.

To be truly RESTful the URI should be a resource identifier and nothing else

Parameters make it RPC over HTTP which is the opposite of the REST paradigm.

http://example.com/{user_id}/posts/first
http://example.com/{user_id}/posts/09/previous
http://example.com/{user_id}/posts/10
http://example.com/{user_id}/posts/11
http://example.com/{user_id}/posts/12
http://example.com/{user_id}/posts/13
http://example.com/{user_id}/posts/14
http://example.com/{user_id}/posts/15/next
http://example.com/{user_id}/posts/last

Using parameters as part of the identifier is a bad practice, regardless of who is doing it.

http://example.com/{user_id}/posts/09/to/04/ <- previous
http://example.com/{user_id}/posts/14/to/19/ <- next

No parameters and more concise and self describing as well.

If the list is totally client side then fragment identifiers would be the correct choice:

Since fragments are never set to the server on requests they are totally client side references. Fragment Identifier is considered a sub-location of the resource, which semantically previous and next are sub-locations of posts/

The specific example of RFC 5147 is applicable here http://example.com/document.txt#line=10,20

RFC7233 specifies the binary RANGE header. That is a very compelling argument for a custom PAGINATION_RANGE header using the RFC5988 Link Header in the same spirit.

http://example.com/{user_id}/posts/first
http://example.com/{user_id}/posts/09/#previous=5
http://example.com/{user_id}/posts/10
http://example.com/{user_id}/posts/11
http://example.com/{user_id}/posts/12
http://example.com/{user_id}/posts/13
http://example.com/{user_id}/posts/14
http://example.com/{user_id}/posts/15/#next=5
http://example.com/{user_id}/posts/last

Fragments do not reload the page but do create history:

So you get correct forward/back button behavior for free!

  • Thanks, so the third option of the ones I suggested? The parameters etc. are just from some popular existing examples and not really relevant to the question. It's just the bit you've called 'varying links' that I'm interested in. – James Allardice May 5 '15 at 20:27
  • the parameters are relevant as they are a poor design choice as they clutter up the API and basically represent RPC over HTTP GET instead of a REST experience. – user7519 May 5 '15 at 20:37
  • I agree, it's relevant to API design in general but not really relevant to my particular question (not my downvote though). Do you know of any public APIs that use the "varying links" technique you've demonstrated? – James Allardice May 6 '15 at 8:28
  • @JarrodRoberson - I've edited my question so I think it's quite a bit clearer. I wasn't asking whether the Link header was the correct solution, or whether query parameters were a good idea. – James Allardice May 6 '15 at 19:16
  • @JamesAllardice: What do you mean by "good?" – Robert Harvey May 6 '15 at 19:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.