Cursor-Based Pagination in SequelizeJS: Practical Examples

Updated: February 22, 2024 By: Guest Contributor Post a comment

Introduction

Cursor-based pagination is widely regarded as a superior method for paging through large sets of data. It offers numerous advantages over traditional offset-based pagination, such as consistent performance regardless of page size and better user experience. SequelizeJS, a Node.js ORM for relational databases, supports cursor-based pagination, though setting it up involves a bit of manual work. This guide will walk you through the process, offering practical examples to implement efficient cursor-based pagination in your SequelizeJS applications.

Prerequisites

  • Basic knowledge of Node.js and JavaScript.
  • Familiarity with SequelizeJS and its concepts.
  • An existing Sequelize model to apply cursor-based pagination to.

Understanding Cursor-Based Pagination

Unlike offset-based pagination, which skips a specified number of records, cursor-based pagination uses a unique identifier (the cursor) from the last row of the current page to fetch the next set of records. This approach mitigates performance issues associated with large datasets and maintains a consistent loading time across pages.

Setting Up Our Project

First, make sure Sequelize and its dependencies are installed in your Node.js project. If not, you can add them using npm:

npm install sequelize pg pg-hstore

This example assumes you’re using PostgreSQL, but Sequelize supports multiple databases.

Implementing Cursor-Based Pagination

To begin, choose a unique and consistently ordered column in your database as the cursor. The ‘createdAt’ or ‘id’ columns are common choices. Here’s how to paginate your data based on the ‘createdAt’ column:

const getUsersPage = async (cursor) => {
  const limit = 10;
  const options = {
    order: [['createdAt', 'ASC']],
    limit,
  };
  if (cursor) {
    options.where = {
      createdAt: {
        [Sequelize.Op.gt]: cursor,
      },
    };
  }
  const users = await User.findAll(options);
  return users;
};

This function retrieves 10 users at a time, starting after the given cursor. It’s a straightforward example of cursor-based pagination in action.

Advanced Cursor Pagination

For more complex scenarios, such as when your data spans across multiple columns with varying order directions, you can expand the logic. Here’s a more elaborate example using the ‘id’ as a cursor:

const getComplexPage = async (cursorId, cursorCreatedAt) => {
  const options = {
    order: [
      ['createdAt', 'DESC'],
      ['id', 'DESC']
    ],
    limit: 10,
    where: {},
  };
  if (cursorId && cursorCreatedAt) {
    options.where = Sequelize.and(
      {
        createdAt: {
          [Sequelize.Op.lt]: cursorCreatedAt,
        },
      },
      {
        id: { [Sequelize.Op.lt]: cursorId },
      }
    );
  }
  const results = await User.findAll(options);
  return results;
};

In this case, the pagination logic becomes more intricate, involving multiple fields to precisely retrieve the next set of data.

Optimizing Performance

While implementing cursor-based pagination, indexing the columns used as cursors cannot be overstated. Proper indexing ensures that the database can quickly access and return the needed data, maintaining high performance as the dataset grows.

Frontend Integration

Integrating cursor-based pagination into your frontend involves altering how you fetch data. Instead of sending a page number, your frontend should send the last record’s cursor received from the backend. This change often simplifies client-side logic and keeps the user experience smooth and responsive.

Conclusion

Cursor-based pagination in SequelizeJS is a robust solution for navigating large datasets efficiently. With the examples and concepts discussed in this guide, you should now have a good foundation for implementing cursor-based pagination in your applications. By carefully selecting your cursor columns and ensuring proper database indexing, you can significantly improve your application’s data retrieval performance and user experience.

As you adopt cursor-based pagination, consider the specific needs of your application and how this approach can best serve those needs. With SequelizeJS, cursor-based pagination is both flexible and powerful, offering the tools needed to handle large datasets effectively.