mcpField
P
pagination

pagination

github.com/claude-dev-suiteDevelopment4638 installsUpdated 2d ago
Repository

About pagination

A reference skill for implementing API pagination, aimed at developers building endpoints. It contrasts cursor, offset, and keyset strategies and includes filtering, sorting, and production guidance.

Key features

  1. 01Cursor, offset, and keyset patterns
  2. 02Filtering and sorting examples
  3. 03Page size and input validation
  4. 04Production checklist

Use cases

  1. 01Choosing pagination for large datasets
  2. 02Adding paging to admin endpoints
  3. 03Reviewing pagination edge cases

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill pagination

After installing, type/paginationin Claude Code to confirm it loaded.

Details

From the project README

Pagination

Cursor-Based (recommended for large datasets)

app.get('/api/products', async (req, res) => {
  const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
  const cursor = req.query.cursor as string | undefined;

  const where: any = {};
  if (cursor) {
    where.id = { gt: cursor };
  }

  const items = await db.product.findMany({
    where,
    take: limit + 1, // Fetch one extra to check hasMore
    orderBy: { id: 'asc' },
  });

  const hasMore = items.length > limit;
  if (hasMore) items.pop();

  res.json({
    data: items,
    pagination: {
      hasMore,
      nextCursor: hasMore ? items[items.length - 1].id : null,
    },
  });
});

Offset-Based (simple, good for small datasets)

app.get('/api/products', async (req, res) => {
  const page = Math.max(parseInt(req.query.page as string) || 1, 1);
  const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
  const offset = (page - 1) * limit;

  const [items, total] = await Promise.all([
    db.product.findMany({ skip: offset, take: limit, orderBy: { createdAt: 'desc' } }),
    db.product.count(),
  ]);

  res.json({
    data: items,
    pagination: {
      page, limit, total,
      totalPages: Math.ceil(total / limit),
      hasMore: offset + items.length < total,
    },
  });
});

Filtering and Sorting

app.get('/api/products', async (req, res) => {
  const { sort = 'createdAt', order = 'desc', category, minPrice, maxPrice, search } = req.query;

  const where: any = {};
  if (category) where.category = category;
  if (minPrice || maxPrice) {
    where.price = {};
    if (minPrice) where.price.gte = parseFloat(minPrice as string);
    if (maxPrice) where.price.lte = parseFloat(maxPrice as string);
  }
  if (search) where.name = { contains: search, mode: 'insensitive' };

  const items = await db.product.findMany({
    where,
    orderBy: { [sort as string]: order },
    take: limit,
    skip: offset,
  });

  res.json({ data: items, pagination: { /* ... */ } });
});

Spring Boot (Pageable)

@GetMapping("/products")
public Page<ProductDto> list(
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "20") int size,
    @RequestParam(defaultValue = "createdAt,desc") String[] sort) {

    Pageable pageable = PageRequest.of(page, Math.min(size, 100),
        Sort.by(Sort.Direction.fromString(sort[1]), sort[0]));
    return productRepo.findAll(pageable).map(mapper::toDto);
}

Comparison

StrategyProsConsBest For
OffsetSimple, jump to pageSlow on large tables, skip driftAdmin panels, small datasets
CursorFast, stable with insertsCan't jump to page NFeeds, infinite scroll, large datasets
KeysetFast, no skip driftComplex multi-column sortTime-series, ordered data

Anti-Patterns

Anti-PatternFix
No max page sizeCap limit (e.g., max 100)
COUNT(*) on huge tablesUse cursor pagination, skip total count
Offset on millions of rowsUse cursor or keyset pagination
Returning all fieldsSelect only needed fields, support fields param
No default sortingAlways define default sort for stable results

Production Checklist

  • Maximum page size enforced (e.g., 100)
  • Default sort order defined
  • Cursor pagination for large/growing datasets
  • Input validation on page/limit/sort params
  • Consistent response envelope (data, pagination)

Comments

Markdown supported