ASP.NET Core 2.2 & 3 REST API #26 — Pagination

Up Next: Health Checks

The approach we are going to take is the simplest one. There are many ways to implement pagination but this is by no means less flexible.

Why do we need pagination? We don’t want to return everything in a response that contains multiple items. The bandwidth is not enough if you’re sending 10’s of thousands items over the wire. Apart from that, there are other concerns: database querying limits and more.

So instead of returning everything, you serve smaller chunks, called pages. Every REST API that implements pagination is going to work pretty much the same way.

We will start by creating 2 contract classes.

The first class is a Response<T> which is going to wrap around every thing we return in our API, except the errors.

So, instead of returning let’s say Ok(new PostResponse(...)) , we are now going to return Ok(new Response<PostResponse>(...) . You can see that the data are returned inside this top-level object.

Let’s say that we want to add some more metadata in each response object, in a compose-able manner, without breaking consumers. This is going to make more sense now that we are going to make a new class called PagedResponse<T> .

We’ve seen examples of using inheritance inside the contracts, we believe this is a bad idea because you introduce unnecessary coupling between them, thus being prone to accidentally affecting others as well. What we are going to do next is not a duck typing solution to maintain compatibility with the Response<T> contract, is’s a completely different one.

We’re taking an IEnumerable<T> as a constructor parameter because paged responses imply multiple items returned.

The page and pageSize are going to be added as query parameters ( ?page=X&pageSize=Y) — remember that query parameters are optional. The nextPage and previousPage properties are pointers to the URL of the next and previous pages.

Now that we’ve got our responses set up now, time to craft our PaginationQuery . We are defaulting the page number to be the first page as well as the page size to be 100. This is really an application-specific constant. If you can or want to handle more pages, feel free to change these. You should also limit your page size on the second constructor and always check that the page number is ≥ 1 in order to avoid any errors.

We can now add a [FromQuery] PaginationQuery paginationQuery inside any endpoint method’s parameters (we are going to use ApiRoutes.Posts.GetAll for now). .NET Core is smart enough to map query parameters to our newly created class by name.

Because this PaginationQuery class is a contract, we need to create a new domain object to map this to. We are going to name this PaginationFilter .

Along with a new mapping entry, as per the previous tutorials.

The next logical step would be to add a var paginationFilter = _mapper.Map<PaginationFilter>(paginationQuery); and then pass this filter to our _postService.GetAllAsync(); method (we are going to change it to accept it).

The way to implement paging with EF Core is very simple. We are just going to skip all the items until the first one of the requested page, and select page size items forward.

Our basic pagination code is in place. You could actually use this as-is, but we are going to take this a step further in order to add the previous and next page ‘pointers’.

We are going to create and register a new service called UriService which is going to be the place where we fabricate any URL that is going to be used in our application.

We need a baseUri private property because our APIs URIs can vary; localhost:someport during development, domain.com:otherport on production and so on.

That’s very simple code, fabricating URIs. Here’s the final controller code:

If the consumer requests no pagination at all, we just return a paged response without additional metadata. Else, we are going to calculate the next and previous pages, as well as make sanity page and page size checks. We extracted this to a different extension method to keep the code’s readability. Here it is:

The last tiny bit is to make sure our base uri is properly dynamic by registering in the MvcInstaller.

You can see that swagger ui has changed to include these 2 new parameters:

Finally, here’s a sample pagination response returning only 5 posts with the next page pointer:

Up Next: Health Checks

Code is available on Github and the instructional videos are located on YouTube.

Keep Coding