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.

Why did we do that?

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, 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




Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Blog Post 3: Prototyping

MetWork/mfext 1.0 is here!

OSI Model (Networking)

Understanding the 12 Agile Principles — #3

On CRM: Why A “Low Code” CRM Could Be A Game Changer For Smaller Companies

Why I coded every day for a year, what I learned, and how you can do it, too.


Azure file share CIFS vs NFS

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Theodoros Ntakouris

Theodoros Ntakouris

More from Medium

Deploy a .NET CORE(5.0/6.0) API on Linux (Ubuntu — EC2 on AWS) — Basic Steps.

Inside ASP.NET Core 6 React Template

Deploy an Angular application with Azure app service

[Video] Polymorphic JSON Serialization (feat. .NET & System.Text.Json)