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 } });
});
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 } });
});
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 } });
});
Pro Tips for 2026
- Always return metadata (total, hasMore)
- Set sensible defaults (limit: 10-20, max: 100)
- Validate inputs
- 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)