BugForge
All lessons
Databases · 07/09 5 min1 hands-on

Pagination that adds up

skip and limit, why you must sort, counting the total, and capping page size.

You never send a whole collection to a client. You send pages.

page 1 → items 1–10    skip 0
page 2 → items 11–20   skip 10
page 3 → items 21–25   skip 20 (a partial page)

So skip = (page - 1) * limit. Using page * limit skips the first page entirely: the most common pagination bug.

The full recipe

const size = Math.min(Math.max(limit, 1), 50);       // clamp: ?limit=100000 can't dump the table
const [items, total] = await Promise.all([
  Item.find().sort({ createdAt: -1 }).skip((page - 1) * size).limit(size),
  Item.countDocuments(),
]);
return { items, page, total, totalPages: Math.ceil(total / size) };

Why sort?

Without sort, the database may return documents in any order, and a different order on each request. Items then repeat or vanish between pages. Always sort, ideally with a unique tiebreaker ({ createdAt: -1, _id: -1 }).

total vs items.length

items.length is the size of this page. The client needs the size of the whole result to draw "Page 2 of 3", so count separately.

Your turn · 1 exercise0/1 solved

Pages that add up

listPage(page, limit) returns { items, page, total, totalPages }. Pages start at 1, items are sorted by n, and limit is capped at 50.

Loading editor…
Next in Databases
Reports with aggregate