DEV Community

JustC
JustC

Posted on

Range Headers: Pagination the HTTP way

Originally published on HackerNoon, this post is now available on dev.to.

This is the second episode of the ReST series! While the first episode centered around semantics, this installment delves into the ways in which request and response headers can enhance interactions, making them more meaningful and comprehensive. For the sake of a focused narrative, we'll concentrate on a common issue: the pagination problem.

In this episode, our primary focus will be on understanding how HTTP Range headers can be employed to address the challenges associated with pagination. By narrowing our scope, we aim to provide a clearer and more in-depth exploration of this specific aspect of RESTful API design.

What Is Pagination?

Simplistically, pagination means splitting large datasets into comprehensible smaller sets and providing means to navigate across these smaller sets. An example would be, say, in some web application, a search for users yields thousands of results.

It would be impractical to overwhelm the poor soul with all results at once. First of all, the convenience of searching for large datasets is no longer a convenience, rather it is a nuisance.

Now, this post may also come as a rant as there are multiple ways to implement it, and the way I feel it should be done is not possible with current specifications.

Pagination Using Query Params

OData specification has support for pagination, and it makes use of query parameters to implement pagination. A typical OData interaction can look as below.

Request:

GET /users?$skip=0&$top=10&$inlinecount=allpages
Enter fullscreen mode Exit fullscreen mode

Response:

200 Okay
X-Some-Side-Channel: count=200
Enter fullscreen mode Exit fullscreen mode

Now, that’s an example of repurposing. Here, the query string is repurposed for pagination. It sounds a bit weird from the get-go. The query string, the name itself, states its purpose “for querying” and is repurposed.

There is a header prefixed X-, and such headers are called custom headers. User-agent needs to understand it, or there is a need for customization in the form of coding at the client end.

Can We Do Better?

RFC2616 had a section on content ranges as below.

14.35.2 Range Retrieval Requests

HTTP retrieval requests using conditional or unconditional GET
methods MAY request one or more sub-ranges of the entity, instead
of the entire entity, using the Range request header, which applies
to the entity returned as the result of the request
Enter fullscreen mode Exit fullscreen mode

Does that sound similar to pagination?

Let’s try to model the earlier OData request using Range Retrieval Request. Ranges are resource-specific meaning /users may be a collection of more than one, user data. A single user is a unit in the collection.

Let’s Ask: What Is a Unit for Users’ Resource?

Request:

OPTION /users
Enter fullscreen mode Exit fullscreen mode

Response:

200 Okay
Accept-Ranges: users
Enter fullscreen mode Exit fullscreen mode

The web application is telling us that users is a unit to specify the range of users. Now, since we got to know that users is the unit, let’s ask for the first 10 users.

Request:

GET /users
Range: users=0-9
Enter fullscreen mode Exit fullscreen mode

Response:

206 Partial Content
Accept-Ranges: users
Content-Range: users 0-9/200


[ 0, …, 9 ]
Enter fullscreen mode Exit fullscreen mode

How does this dialogue work?

What does Request say?

In the request, the user-agent has added a Range header mentioning a specific unit discovered from earlier OPTIONS dialogue and a range in numeric form. Natural, isn’t it?

What does Response say?

The web application responds with 206 clearly stating that the response is partial. Content-Range provides details about the data in the response. In the example, 0-9/200 indicates the first 10 users’ data is being returned out of 200, the number of users satisfying the search query.

It also reiterates the unit being users as Accept-Ranges header. Since Accept-Ranges is reiterated, the OPTIONS call can be avoided altogether as the web application can simply default to sane defaults for the range parameters if the request did not send the Range header.

At times, it might be very intensive to compute the total number of records in the returned collection. In such cases, one can simply respond with * as count. So, the response looks as below.

Response:

206 Partial Content
Accept-Ranges: users
Content-Range: users 0-9/*


[ 0, …, 9 ]
Enter fullscreen mode Exit fullscreen mode

You can ask for multiple ranges too.

Request:

GET /users
Range: users=0-9,35-50
Enter fullscreen mode Exit fullscreen mode

Response:

206 Partial Content
Accept-Ranges: users
Content-Type: multipart/mixed; boundary=PART


--PART
Content-Range: users 0-9
[ 0, …, 9 ]


--PART
Content-Range: 35-50
[ 35, …, 50]
Enter fullscreen mode Exit fullscreen mode

What if the data requested is beyond the range?

The web application can simply state that the data is not available.

Request:

GET /users
Range: users=0-9,35-50
Enter fullscreen mode Exit fullscreen mode

Response:

416 Requested range is not satisfiable
Enter fullscreen mode Exit fullscreen mode

But (The Rant Part)

All this discussion about Range headers might feel like a lot of talk. The potential usefulness of Range headers is, for now, mostly theoretical. While originally mentioned in RFC 2616, the specification explicitly mentions only bytes as the unit for specifying a range. It doesn't necessarily rule out the possibility of using other units, but it also doesn't affirmatively state that it's allowed.

Even in the latest specifications on HTTP, namely RFC7231 and RFC7233, the stance remains unchanged. In reality, the sad truth is that as of now, there are no HTTP servers with native support for custom range units. It's like having a tool in the toolbox that looks promising on paper but turns out to be rarely used in practice.

Whether this will change in the future or if the theoretical potential of Range headers will forever remain just that - theoretical - only time will tell.

Further reading:

Hot sauce if you're wrong - web dev trivia for staff engineers

Hot sauce if you're wrong · web dev trivia for staff engineers (Chris vs Jeremy, Leet Heat S1.E4)

  • Shipping Fast: Test your knowledge of deployment strategies and techniques
  • Authentication: Prove you know your OAuth from your JWT
  • CSS: Demonstrate your styling expertise under pressure
  • Acronyms: Decode the alphabet soup of web development
  • Accessibility: Show your commitment to building for everyone

Contestants must answer rapid-fire questions across the full stack of modern web development. Get it right, earn points. Get it wrong? The spice level goes up!

Watch Video 🌶️🔥

Top comments (0)

The Most Contextual AI Development Assistant

Pieces.app image

Our centralized storage agent works on-device, unifying various developer tools to proactively capture and enrich useful materials, streamline collaboration, and solve complex problems through a contextual understanding of your unique workflow.

👥 Ideal for solo developers, teams, and cross-company projects

Learn more

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay