Node.js + MongoDB
Pagination APIs
Limit, next cursor, and a stable sort — the API shape that matches cursor pagination.
Module 5 is the query: stable sort, $gt on the last key, _id tiebreaker. The API is { items, next }. next is an opaque token (base64 of { price, id }), not ?page=4000. Cap limit. Do not ask the client to send skip.
app.get("/products", async (req, res) => {
const limit = Math.min(Number(req.query.limit) || 10, 50);
const cursor = decodeCursor(req.query.cursor); // null on first page
const filter = { inStock: true };
if (cursor) {
filter.$or = [
{ price: { $gt: cursor.price } },
{ price: cursor.price, _id: { $gt: new ObjectId(cursor.id) } },
];
}
const docs = await products
.find(filter)
.sort({ price: 1, _id: 1 })
.limit(limit + 1)
.toArray();
const hasMore = docs.length > limit;
const page = hasMore ? docs.slice(0, limit) : docs;
const last = page[page.length - 1];
res.json({
items: page.map(({ _id, name, price }) => ({ id: _id.toString(), name, price })),
next: hasMore && last
? encodeCursor({ price: last.price, id: last._id.toString() })
: null,
});
});limit + 1 tells you whether there is another page without a count query. Offset (skip) is acceptable for a 20-row admin table; it is not this endpoint. The token is signed or at least not a raw Mongo filter the client edits into $gt: 0. Index { inStock: 1, price: 1, _id: 1 } or this is a pretty slow JSON wrapper.
Interview question
How do you expose cursor pagination from a Node API?
Stable sort including _id, filter to documents after the decoded cursor, limit N+1 to know if next exists, return items and an opaque next token. I do not take skip from the client on a large collection. The token encodes the sort keys, not a page number.
Practice
The query under a 'next page' of in-stock products.