In this 2-part blog series we will actually go in detail on how you can implement different types of server side pagination with Cosmos DB in your application.
The pagination types that I will cover in these 2 blogs are the following:
- Page number/Page size (aka Skip/Take) pagination
- Next/Previous pagination
I will be implementing these solutions in a single Blazor project with one page per pagination type. I'm going with Blazor because it's quick to setup and so easy to use. I also wanna give it as much more visibility I can as possible because I think it's an incredible project and I wanna see it succeed. Mega props to Steve Sanderson and to Microsoft.
(You can find instructions on how to install it on their GibHub page)
Blazor will allow us to write C# on the front end and have our view automatically update without postbacks whenever our data changes.
Blazor generates 3 projects out of the box and they are more than enough for us to work with.
- Client, is where the views and the pages of our site are located
- Server, is the service that is running to expose the API
- Shared, is where our shared contracts between Client and Server will live
Shared project we will need to install 2 Nuget packages.
Cosmonaut in order to add Cosmonaut support and Cosmonaut.Extensions.Microsoft.DependencyInjection in order to add the dependency injection extensions that we will be using.
Page number/Page size (aka Skip/Take) pagination
This is probably the most common type of pagination you've seen. It's what you are using every time you use Google.
The idea is simple. You have 3 main properties.
- Current page number
- Page size
- Total items
These three properties are enough for us to use in order to generate all the info we need for our Skip/Take pager.
Here is what the class holding the pagination logic looks like:
The reason for the parameter-less constructor and the public setter on the properties is so I didn't have to create another pager DTO and map it in order to use on the front end. However, that's what you'd normally do.
It is not just the Pager that we are serving back though. We also need the entity that we will be retrieving. For this example I will use something simple like a
Student class. It will only have an id, a first name and a last name. It looks like this.
The final model that our endpoint will be returning is a collection of students and the current pagination logic.
The backend code
Now that we have all that set-up let's see how our endpoint will be looking like.
First, we will need to register our
CosmosStore on our
Startup.cs file inside the
Adding those lines allows us to get
ICosmosStore<Student> from .NET Core's dependency injection as shown below.
Our endpoint needs to able to do 2 things.
First it needs to get two query string parameters in order to return data relevant to the paged request. Those parameters are the page number and the page size.
The second thing it needs to do is to query for the total count of the documents in the collection and use that in order to generate the page logic.
Here's the final version of the endpoint.
The frontend code
First we need to add our
functions code in our
As you can see we are using 2 methods and 2 fields.
We are overriding the
OnInitAsync Blazor method which is automatically called on page load and we are calling the
LoadDataAsync method we created to hit our
Server's API endpoint and get our data back in order to visualise it.
Visualising the results with Blazor is as simple as doing the following:
Now every time our
_students field changes in any way, the changes will be automatically reflected in our frontend (GOD I LOVE BLAZOR).
Now the pagination part of the code is a bit more complicated but at it's core it's a series of checks that ensure that we are seeing relevant pagination buttons based on our current state.
At the top we have a input field that allows us to update the page size of the pagination.
Under that we have the pagination numbered buttons including a First, Last, Previous and Next buttons. When the user clicks the button the
LoadDataAsync method is called with the appropriate data and the endpoint is called, returning the new data which are automatically reflected in our web app.
Here is what the end result looks like. Sorry for the poor quality but Giphy didn't keep my original quality (which makes sense to be honest). I hope you can still see what's going on.
Keep in mind this is 100% server side pagination. There is nothing cached on the client side.
Even though this works great with small or limited data sets, it doesn't scale well because the way Cosmonaut adds support for this is by going over each page until it reaches the requested. There are many ways to improve the performance with the most obvious one being caching, but we will actually go in depth on how we can use a combination of page number, page size, continuation token and caching to achieve optimal performance so stay tuned.
In the next part of the series we will look into Next and Previous pagination which are way more efficient and fast.
You can read more about pagination recommendations here.
You can find a live version of the pagination app here: https://cosmosdbpage.azurewebsites.net
You can find the complete solution for this blog post here: https://github.com/Elfocrash/CosmosDBPaginationSample