Skip to content
implementing-query-pagination logo

Implementing Query Pagination

implementing-query-pagination

Implement cursor-based or offset pagination for Prisma queries. Use for datasets 100k+, APIs with page navigation, or infinite scroll/pagination mentions.

SKILL.md

Full skill instructions

QUERIES-pagination: Efficient Pagination Strategies

Teaches correct Prisma 6 pagination patterns with guidance on cursor vs offset trade-offs and performance implications.

<role> Implement cursor-based or offset-based Prisma pagination strategies, choosing based on dataset size, access patterns, and performance requirements. </​role> <when-to-activate> Activates when: user mentions "pagination," "page," "infinite scroll," "load more"; building APIs with page navigation/​list endpoints; optimizing large datasets (100k+) or slow queries; implementing table/​feed views. </​when-to-activate> <overview> **Cursor-based pagination** (recommended): Stable performance regardless of size; efficient for infinite scroll; handles real-time changes gracefully; requires unique sequential ordering field.

Offset-based pagination: Simple; supports arbitrary page jumps; degrades significantly on large datasets (100k+); prone to duplicates/​gaps during changes.

**Core principle: Default to cursor. Use offset only for

small (<10k), static datasets requiring arbitrary page access.** </​overview>

<workflow> ## Pagination Strategy Workflow

Phase 1: Choose Strategy

  • Assess dataset size: <10k (either), 10k–100k (prefer cursor), >100k (require cursor)
  • Assess access: sequential (cursor); arbitrary jumps (offset); infinite scroll (cursor); traditional pagination (cursor)
  • Assess volatility: frequent inserts/​deletes (cursor); static (either)

Phase 2: Implement

  • Cursor: select unique ordering field (id, createdAt+id); implement take+cursor+skip; return next cursor; handle edges
  • Offset: implement take+skip; calculate total pages if needed; validate bounds; document limitations

Phase 3: Optimize & Validate

  • Add indexes on ordering field(s); test with realistic dataset size; measure performance; document pagination metadata in response </​workflow>
<decision-matrix> ## Pagination Strategy Decision Matrix
CriterionCursorOffsetWinner
Dataset > 100kStable O(n)O(skip+n)Cursor
Infinite scrollNaturalPoorCursor
Page controls (1,2,3...)Workaround neededNaturalOffset
Jump to page NNot supportedSupportedOffset
Real-time dataNo duplicatesDuplicates/​gapsCursor
Total count neededExtra querySame queryOffset
ComplexityMediumLowOffset
Mobile feedNaturalPoorCursor
Admin table (<10k)OverkillSimpleOffset
Search resultsGoodAcceptableCursor

Guidelines: (1) Default cursor for user-facing lists; (2) Use offset only for small admin tables, total-count requirements, or arbitrary page jumping in internal tools; (3) Never use offset for feeds, timelines, >100k datasets, infinite scroll, real-time data. </​decision-matrix>

<cursor-pagination> ## Cursor-Based Pagination

Cursor pagination uses a pointer to a specific record as the starting point for the next page.

Basic Pattern

async function getPosts(cursor?: string, pageSize: number = 20) {
  const posts = await prisma.post.findMany({
    take: pageSize,
    skip: cursor ? 1 : 0,
    cursor: cursor ? { id: cursor } : undefined,
    orderBy: { id: 'asc' },
  });

  return {
    data: posts,
    nextCursor: posts.length === pageSize ? posts[posts.length - 1].id : null,
  };
}

Composite Cursor for Non-Unique Ordering

For non-unique fields (createdAt, score), combine with unique field:

async function getPostsByDate(cursor?: { createdAt: Date; id: string }, pageSize: number = 20) {
  const posts = await prisma.post.findMany({
    take: pageSize,
    skip: cursor ? 1 : 0,
    cursor: cursor ? { createdAt_id: cursor } : undefined,
    orderBy: [{ createdAt: 'desc' }, { id: 'asc' }],
  });

  const lastPost = posts[posts.length - 1];
  return {
    data: posts,
    nextCursor:
      posts.length === pageSize ? { createdAt: lastPost.createdAt, id: lastPost.id } : null,
  };
}

Schema requirement:

model Post {
  id        String   @id @default(cuid())
  createdAt DateTime @default(now())
  @@index([createdAt, id])
}

Performance

  • Time complexity: O(n) where n=pageSize (independent of total dataset size); first and subsequent pages identical
  • Index requirement: Critical; without index causes full table scan
  • Memory: Constant (only pageSize records)
  • Data changes: No duplicates/​missing records across pages; new records appear in correct position

</​cursor-pagination>

<offset-pagination> ## Offset-Based Pagination

Offset pagination skips a numeric offset of records.

Basic Pattern

async function getPostsPaged(page: number = 1, pageSize: number = 20) {
  const skip = (page - 1) * pageSize;
  const [posts, total] = await Promise.all([
    prisma.post.findMany({ skip, take: pageSize, orderBy: { createdAt: 'desc' } }),
    prisma.post.count(),
  ]);

  return {
    data: posts,
    pagination: { page, pageSize, totalPages: Math.ceil(total / pageSize), totalRecords: total },
  };
}

Performance Degradation

Complexity: Page 1 O(pageSize); Page N O(N×pageSize)—linear degradation

Real-world example (1M records, pageSize 20):

  • Page 1 (skip 0): ~5ms
  • Page 1,000 (skip 20k): ~150ms
  • Page 10,000 (skip 200k): ~1,500ms
  • Page 50,000 (skip 1M): ~7,500ms

Database must scan and discard skipped rows despite indexes.

When Acceptable

Use only when: (1) dataset <10k OR deep pages rare; (2) arbitrary page access required; (3) total count needed; (4) infrequent data changes. Common cases: admin tables, search results (rarely past page 5), static archives. </​offset-pagination>

<validation> ## Validation
  1. Index verification: Schema has index on ordering field(s); for cursor use @@index([field1, field2]); run npx prisma format

  2. Performance testing:

    console.time('First page');
    await getPosts(undefined, 20);
    console.timeEnd('First page');
    console.time('Page 100');
    await getPosts(cursor100, 20);
    console.timeEnd('Page 100');
    

    Cursor: both ~similar (5–50ms); Offset: verify acceptable for your use case

  3. Edge cases: first page, last page (<pageSize results), empty results, invalid cursor/​page, concurrent modifications

  4. API contract: response includes pagination metadata; nextCursor null when done; hasMore accurate; page numbers

validated (>0); consistent ordering across pages; unique fields in composite cursors </​validation>

<constraints> **MUST**: Index cursor field(s); validate pageSize (max 100); handle empty results; return pagination metadata; use consistent ordering; include unique fields in composite cursors

SHOULD: Default cursor for user-facing lists; limit offset to <100k datasets; document pagination strategy; test realistic sizes; consider caching total count

NEVER: Use offset for >100k datasets, infinite scroll, feeds/​timelines, real-time data; omit indexes; allow unlimited pageSize; use non-unique sole cursor; modify ordering between requests </​constraints>


References

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View