Building a Pagination API with MongoDB

Building a Pagination API with MongoDB

Overview

Pagination is a common technique used to divide large datasets into smaller, more manageable chunks (pages). This is particularly useful for APIs that return data lists, such as blog posts, products, or user records.


MongoDB supports efficient pagination using queries with limit() and skip(), or more efficiently, using range-based pagination (also known as cursor-based pagination) with a sorting key.


Approaches to Pagination

1. Offset-based Pagination (Skip & Limit)

This is the simplest and most common approach.


Example Query:

javascript

Copy

Edit

db.users.find().skip((page - 1) * pageSize).limit(pageSize);

API Example (Node.js with Express):

javascript

Copy

Edit

app.get('/users', async (req, res) => {

    const page = parseInt(req.query.page) || 1;

    const limit = parseInt(req.query.limit) || 10;

    const skip = (page - 1) * limit;


    const users = await db.collection('users')

        .find({})

        .skip(skip)

        .limit(limit)

        .toArray();


    res.json({ page, limit, users });

});

Pros:

Easy to implement.


Works well for small datasets.


Cons:

Performance decreases with large offsets.


Not stable if documents are added or removed between requests (inconsistent results).


2. Cursor-based Pagination (Range-based)

This is more efficient for large datasets and real-time applications.


How It Works:

Sort documents by a unique, indexed field (like _id or a timestamp).


Keep track of the last item seen (cursor).


Fetch the next page based on that field.


Example Query:

javascript

Copy

Edit

db.users.find({ _id: { $gt: lastId } }).limit(pageSize);

API Example:

javascript

Copy

Edit

app.get('/users', async (req, res) => {

    const limit = parseInt(req.query.limit) || 10;

    const lastId = req.query.lastId;


    const query = lastId ? { _id: { $gt: new ObjectId(lastId) } } : {};

    

    const users = await db.collection('users')

        .find(query)

        .sort({ _id: 1 })

        .limit(limit)

        .toArray();


    const nextCursor = users.length > 0 ? users[users.length - 1]._id : null;


    res.json({ users, nextCursor });

});

Pros:

High performance.


Consistent and reliable for dynamic data.


Cons:

Slightly more complex to implement.


Only supports forward navigation unless extra logic is added.


Best Practices for Pagination APIs

Always sort results (e.g., by _id, timestamp, or other indexed field).


Use indexes to improve query performance.


Return metadata like totalCount, currentPage, nextCursor, etc.


Avoid exposing raw _id values in public APIs if possible.


Handle empty results and edge cases gracefully.


Example JSON Response

json

Copy

Edit

{

  "users": [

    { "_id": "66500f1c...", "name": "Alice" },

    { "_id": "66500f1e...", "name": "Bob" }

  ],

  "nextCursor": "66500f1e..."

}

Would you like a working code snippet using a specific language like Python (Flask or FastAPI) or a deeper look into cursor vs offset tradeoffs? 

Learn MERN Stack Course in Hyderabad

Read More

REST vs GraphQL in MERN

Visit Our Quality Thought Training in Hyderabad

Get Directions

Comments

Popular posts from this blog

Understanding Snowflake Editions: Standard, Enterprise, Business Critical

Installing Tosca: Step-by-Step Guide for Beginners

Entry-Level Cybersecurity Jobs You Can Apply For Today