Implementing Pagination

1. Using Skip Offset

const page = 3, size = 20;
await prisma.post.findMany({ skip: (page - 1) * size, take: size });
AspectDetail
TypeOffset-based
CostSlow for large skips

2. Limiting Results

OptionUse
take: NLimit to N rows
take: -NLast N rows (must combine with cursor)

3. Implementing Cursor Pagination

await prisma.post.findMany({
  take: 20,
  skip: 1,                      // skip the cursor row itself
  cursor: { id: lastSeenId },
  orderBy: { id: "asc" }
});
ProDetail
ScalesO(log n) via index seek
StableResilient to inserts/deletes
ConstraintorderBy must be unique/stable

4. Combining Skip and Take

UseDetail
UI page numbersSkip = (page-1)*size
CaveatBecomes slow past tens of thousands

5. Using Distinct Pagination

PatternDetail
distinct + orderByStable distinct rows
Combine with cursorUse first-of-group via window functions in raw SQL for complex cases

6. Creating Infinite Scroll Pattern

Example: Infinite scroll endpoint

async function feed(cursor?: number) {
  const items = await prisma.post.findMany({
    take: 21,
    cursor: cursor ? { id: cursor } : undefined,
    skip:   cursor ? 1 : 0,
    orderBy: { id: "desc" }
  });
  const nextCursor = items.length === 21 ? items.pop()!.id : null;
  return { items, nextCursor };
}
DetailWhy
Take N+1Determine if more pages exist

7. Calculating Total Pages

const total = await prisma.post.count({ where });
const pages = Math.ceil(total / size);
TipDetail
ApproximateUse approx counts on huge tables (PG pg_class.reltuples)

8. Handling Pagination Edge Cases

CaseHandle
Empty pageReturn [] + nextCursor=null
Cursor deletedFallback to next ID gracefully
Backward navUse reversed orderBy + cursor

9. Optimizing Large Dataset Pagination

StrategyDetail
Cursor paginationAvoid OFFSET
Indexed orderByComposite index per sort key
Cache totalsAvoid expensive COUNT(*) per request

10. Using Relay-Style Pagination

ElementDetail
Edges{ node, cursor }
PageInfo{ hasNextPage, endCursor }
CursorBase64-encoded opaque ID