Sling Academy
Home/Node.js/MongooseJS: Cursor-Based Pagination Examples

MongooseJS: Cursor-Based Pagination Examples

Last updated: February 22, 2024

Introduction

When building applications with large datasets, effective pagination strategies become crucial for performance and user experience. MongooseJS, a popular MongoDB object modeling tool for Node.js, supports cursor-based pagination, a method well-suited for scaling and real-time data updates. This tutorial explores how to implement cursor-based pagination with MongooseJS through practical examples.

Understanding Cursor-Based Pagination

Unlike traditional offset-based pagination, which skips a fixed number of documents, cursor-based pagination uses a pointer (“cursor”) to traverse through documents. This approach offers several advantages, including constant query performance regardless of page number and a stable view of data even when new records are added or existing ones are modified.

Setting Up the Environment

To follow along, ensure you have Node.js and MongoDB installed. Initialize a new Node.js project and install Mongoose:

$ npm init -y
$ npm install mongoose

Next, connect Mongoose to your MongoDB instance:

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/yourDB', {
 useNewUrlParser: true,
 useUnifiedTopology: true
});

Model Definition

Define a simple model to work with. For example, a blog post schema:

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const postSchema = new Schema({
 title: { type: String, required: true },
 body: { type: String, required: true },
 createdAt: { type: Date, default: Date.now }
});

const Post = mongoose.model('Post', postSchema);

Basic Cursor-Based Pagination

To implement cursor-based pagination, you start by querying a set amount of documents, using the _id or another field as your cursor. Here’s a function to fetch posts:

async function fetchPosts(cursor, limit = 10) {
 let query = {};
 if (cursor) {
 query._id = { $gt: cursor };
 }
 return await Post.find(query).limit(limit);
}

This function retrieves a fixed number of documents (limit) that come after the specified cursor.

Improving Performance with Indexes

To ensure queries run efficiently, especially on large datasets, it’s crucial to add indexes on the fields used for pagination (e.g., _id or createdAt):

postSchema.index({ createdAt: 1 });

This index supports efficient retrieval by createdAt order.

Advanced Cursor-Based Pagination

An advanced strategy involves using timestamps for cursor pagination. Assuming posts have a `createdAt` field, you could paginate based on document creation time:

async function fetchPostsByTime(cursor, limit = 10) {
 let query = {};
 if (cursor) {
 query.createdAt = { $gt: cursor };
 }
 return await Post.find(query).sort({ createdAt: 1 }).limit(limit);
}

This method offers a chronological view, ideal for real-time data feeds.

Tips for Effective Cursor-Based Pagination

  • Choose appropriate cursor fields: While _id is a common and easy choice, other fields like timestamps can be more suitable depending on the use case.
  • Communicate with frontend clearly: Ensure the frontend is aware of how cursors are passed and handled. Cursors can be opaque strings or specific field values.
  • Implement client-side caching: For frequently accessed pages, caching the results on the client side can significantly improve the user experience.

Conclusion

Cursor-based pagination in MongooseJS improves user experience and application performance, especially for large and continuously updating datasets. By following the outlined examples and best practices, developers can effectively implement this pagination strategy in their applications.

Next Article: Fix: Mongoose Duplicate Key Error with Upsert

Previous Article: Mongoose: How to populate a subdocument after creation

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
  • 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
  • ExpressJS: How to pass variables to Pug templates