Implementing pagination for API responses

Loading

Implementing pagination in API responses is essential for efficiently handling large datasets and improving performance. Below, I’ll guide you through different types of pagination and how to implement them in a REST API using Node.js (Express.js) and MongoDB.


Types of Pagination

1. Offset-Based Pagination

  • Uses limit and offset parameters.
  • Best for simple datasets but inefficient for large data sets.

Request Example:

GET /api/items?limit=10&offset=20
  • limit: Number of records per request.
  • offset: Number of records to skip.

Implementation in Node.js (Express.js)

const express = require('express');
const mongoose = require('mongoose');

const app = express();
mongoose.connect('mongodb://localhost:27017/testdb');

const ItemSchema = new mongoose.Schema({ name: String });
const Item = mongoose.model('Item', ItemSchema);

app.get('/api/items', async (req, res) => {
let { limit = 10, offset = 0 } = req.query;
limit = parseInt(limit);
offset = parseInt(offset);

const items = await Item.find().skip(offset).limit(limit);
res.json({ data: items, limit, offset });
});

app.listen(3000, () => console.log('Server running on port 3000'));

Pros: Simple to implement.
Cons: Slows down for large datasets because of .skip() usage.


2. Cursor-Based Pagination

  • Uses cursors (IDs or timestamps) instead of offsets.
  • More efficient for large datasets than offset-based pagination.

Request Example:

GET /api/items?limit=10&cursor=65ffb1e39ac2e9f2d8d3e123
  • cursor: ID of the last retrieved item.

Implementation in Node.js (Express.js)

app.get('/api/items', async (req, res) => {
let { limit = 10, cursor } = req.query;
limit = parseInt(limit);

let query = cursor ? { _id: { $gt: cursor } } : {};
const items = await Item.find(query).limit(limit);

const nextCursor = items.length ? items[items.length - 1]._id : null;

res.json({ data: items, nextCursor });
});

Pros: Faster for large datasets.
Cons: Can’t jump to arbitrary pages.


3. Page-Based Pagination

  • Uses page numbers instead of offsets.
  • Works similar to offset pagination.

Request Example:

GET /api/items?page=3&limit=10
  • page: The current page number.
  • limit: Number of records per request.

Implementation in Node.js (Express.js)

app.get('/api/items', async (req, res) => {
let { limit = 10, page = 1 } = req.query;
limit = parseInt(limit);
page = parseInt(page);

const skip = (page - 1) * limit;
const items = await Item.find().skip(skip).limit(limit);

res.json({ data: items, page, limit });
});

Pros: Easy to implement and user-friendly.
Cons: Slower for large datasets (similar to offset pagination).


4. Keyset Pagination

  • Uses sorted unique keys instead of offsets.
  • Best for time-sensitive data (e.g., social media feeds, logs).

Request Example:

GET /api/items?limit=10&lastCreatedAt=2024-03-27T12:00:00.000Z
  • lastCreatedAt: Timestamp of the last item retrieved.

Implementation in Node.js (Express.js)

app.get('/api/items', async (req, res) => {
let { limit = 10, lastCreatedAt } = req.query;
limit = parseInt(limit);

let query = lastCreatedAt ? { createdAt: { $lt: new Date(lastCreatedAt) } } : {};
const items = await Item.find(query).sort({ createdAt: -1 }).limit(limit);

const nextLastCreatedAt = items.length ? items[items.length - 1].createdAt : null;

res.json({ data: items, nextLastCreatedAt });
});

Pros: Fast for time-ordered data.
Cons: Doesn’t support jumping to arbitrary pages.


Best Pagination Approach for Different Use Cases

Pagination TypeBest ForProsCons
Offset-BasedSmall datasets, Simple UIEasy to implementSlow for large data
Cursor-BasedLarge datasets, Real-time appsFaster, scalableNo jumping to pages
Page-BasedPaginated UI (e.g., eCommerce)User-friendlySlower for large data
Keyset-BasedTime-sensitive data (e.g., feeds, logs)FastNo jumping to pages

Leave a Reply

Your email address will not be published. Required fields are marked *