DEV Community

1xApi
1xApi

Posted on • Originally published at 1xapi.com

How to Implement API Pagination in Node.js (2026 Guide)

How to Implement API Pagination in Node.js (2026 Guide)

As of February 2026, APIs handle more data than ever. Whether you are building a SaaS platform, a mobile app backend, or a public API service, pagination is not optional — it is essential for performance, user experience, and cost control.

Offset-Based Pagination

Offset pagination uses limit and offset parameters.

app.get("/api/users", async (req, res) => {
  const limit = Math.min(parseInt(req.query.limit) || 10, 100);
  const offset = parseInt(req.query.offset) || 0;

  const [users, total] = await Promise.all([
    db.user.findMany({ take: limit, skip: offset, orderBy: { createdAt: 'desc' } }),
    db.user.count(),
  ]);

  res.json({ data: users, pagination: { total, limit, offset, hasMore: offset + limit < total } });
});
Enter fullscreen mode Exit fullscreen mode

Pros: Easy to implement, bookmarkable URLs
Cons: Slow with large offsets, inconsistent with changing data

Cursor-Based Pagination

Cursor pagination uses an opaque cursor for better performance at scale.

app.get("/api/users", async (req, res) => {
  const limit = Math.min(parseInt(req.query.limit) || 10, 100);
  const cursor = req.query.cursor ? { id: Buffer.from(req.query.cursor, 'base64').toString() } : undefined;

  const users = await db.user.findMany({
    take: limit + 1,
    cursor: cursor ? { id: cursor } : undefined,
    skip: cursor ? 1 : 0,
    orderBy: { createdAt: 'desc' }
  });

  const hasMore = users.length > limit;
  const data = hasMore ? users.slice(0, -1) : users;
  const nextCursor = hasMore ? Buffer.from(users[users.length - 2].id).toString('base64') : null;

  res.json({ data, pagination: { hasMore, nextCursor } });
});
Enter fullscreen mode Exit fullscreen mode

Pros: Consistent performance, works great with real-time data
Cons: Cannot jump to specific pages

Keyset Pagination (Twitter/Facebook style)

app.get("/api/users", async (req, res) => {
  const limit = parseInt(req.query.limit) || 10;
  const lastCreatedAt = req.query.before ? new Date(req.query.before) : new Date();

  const users = await db.user.findMany({
    take: limit + 1,
    where: { createdAt: { lt: lastCreatedAt } },
    orderBy: { createdAt: 'desc' }
  });

  const hasMore = users.length > limit;
  res.json({ data: hasMore ? users.slice(0, -1) : users, pagination: { hasMore } });
});
Enter fullscreen mode Exit fullscreen mode

Pro Tips for 2026

  1. Always return metadata (total, hasMore)
  2. Set sensible defaults (limit: 10-20, max: 100)
  3. Validate inputs
  4. Document pagination in your OpenAPI spec

When to Use What

  • Small datasets (<10k rows): Offset pagination
  • Large datasets / real-time: Cursor pagination
  • Infinite scroll / feeds: Keyset pagination
  • Analytics / reports: Offset with totals

Ready to build high-performance APIs? At 1xAPI, we provide production-ready APIs for football data, movies, and more.

Top comments (0)