DEV Community

Ayanabilothman
Ayanabilothman

Posted on

DO NOT use 'skip' to paginate in mongoose 🫣

In this post, we won't talk about pagination definition, although we will go through how to perform pagination with another approach. It's preferred to have a background of what index is in databases.

In usual, there is no web application without a pagination feature. As it will be madness if we decide to display all the documents existing in the database on one single page! 😅

And the reason behind this is the cost of reading all documents all at once from the database, and send this duty heavy payload through the response.

Usually, the following code is used to perform pagination using mongoose in nodejs

await User.find().skip(0).limit(10)
Enter fullscreen mode Exit fullscreen mode

This statement will query the users in the User model and select the first 10 documents but without skipping any document as the value for skip method is 0.

But what if we need the 'second' 10 documents, i.e. the 11th document to the 20th. We will rewrite the above statement to be 👇

await User.find().skip(10).limit(10)
Enter fullscreen mode Exit fullscreen mode

The cost behind this statment, that it will scan all documents from the beginning to only be able to 'skip' whatever the number you pass as an argument! That means if you need, for instance, to skip the first 100 documents, it will go through all these documents as well to be able to skip them!

As a result, performance decreases as the number you pass increases because using 'skip' will make the query take longer to execute. 😕

So, what is the alternative approach 🤔?

Simply, we can use indexes and work with range queries to prevent scanning unneeded documents.

We can apply this by using a combination between sort method and $lt or $gt operator, but you should pass an indexed field as _id to the sort method .

This approach is called Cursor-based pagination

Cursor-based pagination works by returning a cursor as an ObjectId or a timestamp that represents the position of the last document in the current page. The next page can then be retrieved by passing this cursor to the query, which will return the documents that come after the cursor.
For example 👇

await User.find( { _id: { $gt: currentCursor } } ).sort('_id').limit(10)
Enter fullscreen mode Exit fullscreen mode

And to be accurate this approach also has a downside 😞, what if any of the documents in the User model is updated or deleted! The cursor may not be accurate in the case, and by fault, some documents could be ignored!

So finally, we could say that these are two different approaches to perform pagination, and each one has its downsides, so you should carefully choose which one will be better depending on the case you work on. 😄

Top comments (0)