Sling Academy
Home/Node.js/How to Implement Pagination in Mongoose

How to Implement Pagination in Mongoose

Last updated: December 30, 2023

Pagination is a vital feature when dealing with large datasets in a MongoDB database using Mongoose ORM in a Node.js environment. Implementing efficient pagination can significantly enhance performance and usability. Below are three solutions for implementing pagination in Mongoose, demonstrated using up-to-date JavaScript syntax and patterns.

Offset/Limit Pagination

This traditional approach involves skipping a set number of records (offset) and then limiting the number of records returned:

  • Decide the page size and calculate the offset based on the page number.
  • Use the .skip() and .limit() methods in Mongoose to paginate the results.

Code example:

const mongoose = require('mongoose');
const { Schema } = mongoose;

const PostSchema = new Schema({ /* schema definition */ });
const Post = mongoose.model('Post', PostSchema);

async function fetchPosts(page, pageSize) {
  const offset = (page - 1) * pageSize;
  return await Post.find().skip(offset).limit(pageSize);
}

Pros: Simple to implement and understand.

Cons: Performance issues with large datasets due to high offset values.

Cursor Based Pagination

Using a unique identifier or cursor to fetch records after the cursor:

  • Perform initial query to retrieve the first set of results along with a cursor (usually the _id).
  • For consecutive pages, query the next set of results by finding records where the cursor is greater than the last cursor provided (for ascending order).

Code example:

const fetchPostsAfterCursor = async (cursor, pageSize) => {
  return cursor
    ? await Post.find({ _id: { $gt: cursor } }).limit(pageSize)
    : await Post.find().limit(pageSize);
};

Pros: Better performance especially with larger datasets and is the preferred approach for infinite scrolling implementations.

Cons: Cannot directly jump to a specific page, more complex to implement than offset/limit.

Aggregation Framework Pagination

Utilizing the MongoDB aggregation framework for more complex queries. Here’re the steps to follow:

  • Build an aggregation pipeline that includes the pagination logic.
  • Perform operations such as matching (filtering), sorting, skipping, and limiting.

Example:

const fetchPostsUsingAggregation = async (page, pageSize) => {
  const skips = pageSize * (page - 1);
  return Post.aggregate([
    { $match: {}},
    { $sort: { createdAt: -1 }},
    { $skip: skips },
    { $limit: pageSize }
  ]); 
};

Pros: Extremely flexible, allowing complex queries and transformations within the pagination.

Cons: More intricate and involves a steeper learning curve to utilize correctly compared to other methods.

Final Words

Pagination in Mongoose allows you to handle large datasets effectively without overwhelming your server or your users. Each method has its specific use case; the right choice depends on your dataset size, the required user experience, and the complexity of your queries. Offset/limit is straightforward but slows down with large datasets, cursor-based is timely for large datasets with smoother user experience needs, and the aggregation framework offers the most flexibility at the cost of complexity.

Next Article: Sorting results in Mongoose (ASC and DESC)

Previous Article: Understanding Mongoose Model.deleteOne() Method with Examples

Series: Mongoose.js Tutorials

Node.js

You May Also Like

  • NestJS: How to create cursor-based pagination (2 examples)
  • Cursor-Based Pagination in SequelizeJS: Practical Examples
  • MongooseJS: Cursor-Based Pagination Examples
  • Node.js: How to get location from IP address (3 approaches)
  • SequelizeJS: How to reset auto-increment ID after deleting records
  • SequelizeJS: Grouping Results by Multiple Columns
  • NestJS: Using Faker.js to populate database (for testing)
  • NodeJS: Search and download images by keyword from Unsplash API
  • NestJS: Generate N random users using Faker.js
  • Sequelize Upsert: How to insert or update a record in one query
  • NodeJS: Declaring types when using dotenv with TypeScript
  • Using ExpressJS and Multer with TypeScript
  • NodeJS: Link to static assets (JS, CSS) in Pug templates
  • NodeJS: How to use mixins in Pug templates
  • NodeJS: Displaying images and links in Pug templates
  • ExpressJS + Pug: How to use loops to render array data
  • ExpressJS: Using MORGAN to Log HTTP Requests
  • NodeJS: Using express-fileupload to simply upload files
  • ExpressJS: How to render JSON in Pug templates