Pagination, Filtering & Sorting

🔗 REST API Lesson 9 Intermediate

Returning thousands of database records in a single response is a common performance mistake. Pagination, filtering, and sorting make your API efficient and developer-friendly for large datasets.

1 Pagination Strategies
  • Offset Pagination: Use page and limit params. Simple but can miss records on large, fast-changing datasets.
  • Cursor Pagination: Uses a cursor (last seen ID) for efficient, consistent pagination on large collections. Used by Facebook, Twitter.
JavaScript — Offset Pagination (Express + Mongoose)
router.get("/", async (req, res) => {
  // Parse pagination params with safe defaults
  const page  = Math.max(1, parseInt(req.query.page)  || 1);
  const limit = Math.min(100, parseInt(req.query.limit) || 20);
  const skip  = (page - 1) * limit;

  // Build filter object from query params
  const filter = {};
  if (req.query.category) filter.category = req.query.category;
  if (req.query.inStock)  filter.inStock  = req.query.inStock === "true";

  // Build sort (e.g. ?sort=-price,name means price DESC, name ASC)
  const sortStr = req.query.sort || "-createdAt";
  const sort = {};
  sortStr.split(",").forEach(field => {
    if (field.startsWith("-")) sort[field.slice(1)] = -1;
    else sort[field] = 1;
  });

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

  res.json({
    success: true,
    data,
    meta: {
      page, limit, total,
      totalPages: Math.ceil(total / limit),
      hasNextPage: page * limit < total,
      hasPrevPage: page > 1
    }
  });
});
2 Query Parameter Conventions
REST — Query Parameter Patterns
# Pagination
GET /api/v1/products?page=2&limit=20

# Filtering
GET /api/v1/products?category=electronics&inStock=true
GET /api/v1/products?minPrice=1000&maxPrice=5000

# Sorting (prefix - for descending)
GET /api/v1/products?sort=-price          # price DESC
GET /api/v1/products?sort=name,-createdAt # name ASC, createdAt DESC

# Field selection (sparse fieldsets)
GET /api/v1/products?fields=id,name,price

# Search
GET /api/v1/products?q=laptop

# Combined
GET /api/v1/products?category=laptops&sort=-price&page=1&limit=10
3 Code Challenge
Challenge: Implement cursor-based pagination for a GET /messages endpoint. Use the last message's _id as the cursor, and return a nextCursor in the response metadata for the client to use in the next request.