Script Valley
REST API Development: Complete Course from Beginner to Production
Advanced Concepts: Pagination, Filtering, Versioning, and Rate LimitingLesson 5.1

Pagination Strategies: Page-Based and Cursor-Based

pagination, page-based pagination, cursor-based pagination, offset pagination, limit offset, pagination metadata, performance

Pagination Strategies: Page-Based and Cursor-Based

No REST API should return unbounded lists of data. Returning 100,000 records in a single response will crash clients, exhaust server memory, and saturate network bandwidth. Pagination divides large result sets into manageable pages, and choosing the right strategy has significant performance implications.

Page-Based (Offset) Pagination

Page-based pagination uses a page number and limit: GET /api/posts?page=3&limit=20. This skips (page-1)*limit records and returns the next limit records.

const page = parseInt(req.query.page) || 1;
const limit = Math.min(parseInt(req.query.limit) || 20, 100);
const skip = (page - 1) * limit;

const [data, total] = await Promise.all([
  Post.find(filter).skip(skip).limit(limit).sort(sort),
  Post.countDocuments(filter)
]);

res.json({
  success: true,
  data,
  meta: {
    total,
    page,
    limit,
    totalPages: Math.ceil(total / limit),
    hasNextPage: page * limit < total,
    hasPrevPage: page > 1
  }
});

Limitation: OFFSET-based pagination is slow on large datasets because the database must scan all preceding rows even if it does not return them. Records can also appear twice or be skipped if data is inserted or deleted between requests.

Cursor-Based Pagination

Cursor-based pagination uses the ID or timestamp of the last seen record as a cursor, fetching the next N records after it. It is more efficient and consistent for large, frequently changing datasets.

const { after, limit = 20 } = req.query;
const query = after ? { _id: { $gt: after } } : {};

const data = await Post.find({ ...filter, ...query })
  .limit(parseInt(limit) + 1)
  .sort({ _id: 1 });

const hasNextPage = data.length > limit;
if (hasNextPage) data.pop();

res.json({
  data,
  meta: {
    hasNextPage,
    nextCursor: hasNextPage ? data[data.length - 1]._id : null
  }
});

Up next

Filtering, Sorting, and Searching

Sign in to track progress

Pagination Strategies: Page-Based and Cursor-Based โ€” Advanced Concepts: Pagination, Filtering, Versioning, and Rate Limiting โ€” REST API Development: Complete Course from Beginner to Production โ€” Script Valley โ€” Script Valley