home / skills / secondsky / claude-skills / api-pagination

This skill helps you implement scalable API pagination using offset, cursor, and keyset strategies to optimize large datasets.

npx playbooks add skill secondsky/claude-skills --skill api-pagination

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
2.1 KB
---
name: api-pagination
description: Implements efficient API pagination using offset, cursor, and keyset strategies for large datasets. Use when building paginated endpoints, implementing infinite scroll, or optimizing database queries for collections.
---

# API Pagination

Implement scalable pagination strategies for handling large datasets efficiently.

## Pagination Strategies

| Strategy | Best For | Performance |
|----------|----------|-------------|
| Offset/Limit | Small datasets, simple UI | O(n) |
| Cursor | Infinite scroll, real-time | O(1) |
| Keyset | Large datasets | O(1) |

## Offset Pagination

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

  const [products, total] = await Promise.all([
    Product.find().skip(offset).limit(limit),
    Product.countDocuments()
  ]);

  res.json({
    data: products,
    pagination: {
      page,
      limit,
      total,
      totalPages: Math.ceil(total / limit)
    }
  });
});
```

## Cursor Pagination

```javascript
app.get('/posts', async (req, res) => {
  const limit = 20;
  const cursor = req.query.cursor;

  const query = cursor
    ? { _id: { $gt: Buffer.from(cursor, 'base64').toString() } }
    : {};

  const posts = await Post.find(query).limit(limit + 1);
  const hasMore = posts.length > limit;
  if (hasMore) posts.pop();

  res.json({
    data: posts,
    nextCursor: hasMore ? Buffer.from(posts[posts.length - 1]._id).toString('base64') : null
  });
});
```

## Response Format

```json
{
  "data": [...],
  "pagination": {
    "page": 2,
    "limit": 20,
    "total": 150,
    "totalPages": 8
  },
  "links": {
    "first": "/api/products?page=1",
    "prev": "/api/products?page=1",
    "next": "/api/products?page=3",
    "last": "/api/products?page=8"
  }
}
```

## Best Practices

- Set reasonable max limits (e.g., 100)
- Use cursor pagination for large datasets
- Index sorting fields
- Avoid COUNT queries when possible
- Never allow unlimited page sizes

Overview

This skill implements efficient API pagination using offset, cursor, and keyset strategies for large datasets. It provides production-ready patterns and response formats to support paginated endpoints, infinite scroll, and performance-sensitive collection queries. The implementation is TypeScript-friendly and designed for integration with modern stacks like Cloudflare, React, and Tailwind-based frontends.

How this skill works

The skill supplies three pagination strategies: offset/limit for simple pages, cursor pagination for infinite scroll and real-time feeds, and keyset pagination for high-performance access to large collections. Each strategy includes request parsing, safe limits, and a consistent response shape with data, pagination metadata, and navigation links. Cursor logic encodes/decode opaque cursors; keyset relies on indexed sort keys to avoid OFFSET scans.

When to use it

  • Offset/limit when dataset is small and UI requires numbered pages.
  • Cursor pagination for infinite scroll or APIs that must provide stable forward traversal.
  • Keyset pagination for very large tables where OFFSET is too slow.
  • When you need consistent response metadata and navigation links for clients.
  • When you must enforce maximum page sizes and protect the database from heavy queries.

Best practices

  • Enforce a reasonable max limit (e.g., 100) to prevent heavy responses.
  • Prefer cursor or keyset for large datasets to achieve O(1) performance.
  • Index the fields used for sorting and keyset lookups.
  • Avoid count queries on very large tables; supply approximate totals or defer expensive counts.
  • Encode cursors opaquely (e.g., base64) and validate them on input.

Example use cases

  • Products listing endpoint with offset pagination and total pages for e-commerce.
  • Social feed using cursor pagination to support infinite scrolling in a client app.
  • Audit log or events listing using keyset pagination to page through millions of rows quickly.
  • API that returns standardized pagination metadata and HATEOAS-style links for navigation.
  • Protecting backend resources by capping page size and preventing unbounded requests.

FAQ

When should I choose cursor over offset pagination?

Choose cursor when you need stable, performant forward traversal (infinite scroll) or when OFFSET becomes slow for large offsets.

How do I generate a safe cursor?

Use an opaque encoding like base64 of the last item's indexed key or id, and validate/normalize it server-side before using it in queries.