Your findIndex(item => item.id >= cursor) is currently doing inclusive paging, not start-after-cursor paging. That repeats the previous page’s last item instead of moving past it.
A cursor here should mean “start after this id,” so the comparison needs to be exclusive. There’s also a small edge case: if nothing is greater than cursor, findIndex returns -1, and slice(-1, .) pulls from the end of the array. Tiny mental model: the cursor is a fencepost, not a bookmark.
The smallest fix is this:
function getPage(items, cursor, limit) {
let start = 0;
if (cursor !== null) {
start = items.findIndex(item => item.id > cursor);
if (start === -1) start = items.length;
}
const page = items.slice(start, start + limit);
const nextCursor = page.length . page[page.length - 1].id : null;
return { page, nextCursor };
}
Tiny version of the boundary change:
item.id > cursor
With item.id > cursor and start = items.length in place:
item.id > cursor skips the last item from the previous page.
start = items.length ensures an exhausted cursor returns an empty page instead of the tail item.
That gives you stable page boundaries as long as items are sorted by unique id.
You can see nextCursor behave correctly here:
const items = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }];
function getPage(items, cursor, limit) {
let start = 0;
if (cursor !== null) {
start = items.findIndex(item => item.id > cursor);
if (start === -1) start = items.length;
}
const page = items.slice(start, start + limit);
const nextCursor = page.length . page[page.length - 1].id : null;
return { page, nextCursor };
}
const first = getPage(items, null, 2);
console.log(first.page.map(x => x.id), first.nextCursor); // [1, 2] 2
const second = getPage(items, first.nextCursor, 2);
console.log(second.page.map(x => x.id), second.nextCursor); // [3, 4] 4
const third = getPage(items, second.nextCursor, 2);
console.log(third.page.map(x => x.id), third.nextCursor); // [5] 5
const fourth = getPage(items, third.nextCursor, 2);
console.log(fourth.page.map(x => x.id), fourth.nextCursor); // [] null
If inserts can happen between requests, make sure the cursor key is immutable and unique.
WaffleFries