CHAPTER 6
It's almost always a bad idea to return every available resource to the client at once. A technique that is usually referred to as pagination (or sometimes as paging mechanism, especially on webpages) is used for displaying a limited number of results where a returned data set is too big to be displayed in one place or at once.
Usually whenever pagination is implemented, this is followed by the information on how to get the next result set or how to get to the beginning of the list. RESTful services are not much different in this sense. It is possible to create a representation of resources by using pagination.
When building REST web services, we usually think about resources. The first question to pose is if the page or pagination is a resource on its own. In my opinion it is not, and, instead, the information about the pagination is just an integrated part of the resource itself. In other words, the information about how to page through, let’s say a collection of Products, will be embedded in the Product collection itself.
There are mainly three locations to place the information about pagination:
As previously mentioned, the first option is not the best idea because pagination is not a resource (this option, through the URI, looks like a page is a resource). So, I would generally skip this way of representing the pagination.
The second option represents a standard way of solving this problem as it enables the encoding of paging application directly in the query string. In our examples, we are going to use this approach.

In this chapter, we are going to see how to implement pagination for the already existing Products that we have implemented in the ProductService. The pagination makes sense only when returning a list of objects and, therefore, the perfect candidate for us would be the GET /products method which returns the list of available products.
There are several ways and styles of expressing the paging components when it comes to the naming convention for the query string parameters. In some examples, you will see offset and limit being mentioned. In other examples, you will see skip and take. But I will use a more intuitive and simple way, which is page and size:
In order to inform the client application that there is a way to navigate through pages, we will use the Links section. Discoverability is part of the aforementioned HATEOAS constraint. The basic idea is the service exposes links to resources available. In our case, they are next, previous, first, and last so that the client is able to discover the URI associated with the resource and to further navigate to the resource. It is a bit similar to navigating a webpage through a browser. The links are the shown to us and, by following the links, we can discover new resources (in this case, pages).
Actually, the logic is straightforward; the client will know which URI is available through the Link.Rel attribute.
Link.Rel | Description |
|---|---|
next | Goes to the next page. |
previous | Goes to the previous page. |
first | Goes to the first page, which is the beginning of the list. |
last | Goes to the last page, which is the end of the list. |
You might remember the GetProducts object that was used to retrieve a list of Products. We will expand this object to contain the two aforementioned query string parameters, and we will add a List<Link> property to the ProductsResponse.
|
{ public int? Page { get; set; } public int? Size { get; set; } } { public ProductsResponse() { Links = new List<Link>(); } public List<ProductResponse> Products { get; set; } public List<Link> Links { get; set; } } public class ProductResponse { public ProductResponse() { Links = new List<Link>(); } public int Id { get; set; } public string Name { get; set; } public Status Status { get; set; } public List<Link> Links { get; set; } } |
In order to add some structure to the pagination, we introduce in the data access layer a new class, PagedListResult.[23] An instance of this class will be returned from the repository when searching for products. The PagedListResult class is implemented as follows.
ProductRepository contains a new method called GetPaged(). As shown in the following code example, instead of only returning the products, we will enrich the object with extra information such as HasNext, HasPrevious, and Count, which represents the total number of items. This will later enable the service to properly create page links. The implementation is basic; it doesn’t contain any code regarding sorting fields and direction in order to keep the example simple. The intention is not to show how to implement the repository code, but rather how to wire all the pieces together.
As shown previously when we looked at the ProductService, we will extend the ProductMapper class with a new method called ToProductsResponse() to which we will pass the PagedListResult<Product> structure. I have omitted the actual Links generation logic to keep the code succinct; I show only the NextLink() method. But the important thing to note here is that we will generate extra links as explained in the Discoverability section.
Getting the list of products now requires two scenarios. In the first scenario, we want the paging, and in the second scenario, all the data is returned. This is not necessarily a real-world scenario, but I think that it is important to show this kind of implementation as well. If the page and size parameters are not supplied, the Get method will return the full list of products. Otherwise, the paging will kick in.
And finally, when retrieving data, we can see that the links get updated every time there are available products. The response of the web service would look like the following if we were requesting GET /products?page=1&size=100.
<ProductsResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <Links> <Link> <Href>products?page=1&size=100</Href> <Rel>self</Rel> <Title>self</Title> <Type i:nil="true"/> </Link> <Link> <Href>products?page=2&size=100</Href> <Rel>next</Rel> <Title>next</Title> <Type i:nil="true"/> </Link> <Link> <Href>products?page=1&size=100</Href> <Rel>first</Rel> <Title>first</Title> <Type i:nil="true"/> </Link> <Link> <Href>products?page=10&size=100</Href> <Rel>last</Rel> <Title>last</Title> <Type i:nil="true"/> </Link> </Links> <Products><!--omitted for brevity--></Products> </ProductsResponse> |