> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usehasp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pagination

> Cursor-based pagination with manual listRecords or the paginateRecords async generator.

HASP uses **cursor-based pagination** exclusively. Never use offset-based pagination.

## Manual Pagination

```javascript theme={null}
// First page — omit cursor
const page1 = await sdk.listRecords('tasks', {
  pageSize: 25,
  sort: 'created_at:desc',
});

// page1.meta.hasMore  — true if more pages exist
// page1.meta.cursor   — pass to next request
// page1.meta.total    — total count (omitted for apps with more than ~10,000 records)

// Next page — pass cursor from previous response
if (page1.meta.hasMore) {
  const page2 = await sdk.listRecords('tasks', {
    pageSize: 25,
    sort: 'created_at:desc',
    cursor: page1.meta.cursor,
  });
}
```

## paginateRecords(entityKey, options?) — Async Generator

The SDK provides an async generator that handles cursor passing automatically:

```javascript theme={null}
for await (const record of sdk.paginateRecords('tasks', {
  filter: { status: { eq: 'open' } },
  sort: 'due_date:asc',
  pageSize: 50,
})) {
  renderTask(record);
}
```

### Options

| Option       | Type          | Description                                 |
| ------------ | ------------- | ------------------------------------------- |
| `filter`     | Filter object | See [Filtering](/app-builder/sdk/filtering) |
| `sort`       | string        | `"fieldKey:asc"` or `"fieldKey:desc"`       |
| `pageSize`   | number        | Records per page (max 100, default 25)      |
| `maxPages`   | number        | Stop after N pages                          |
| `maxRecords` | number        | Stop after N records total                  |
| `signal`     | AbortSignal   | Cancel iteration on navigation/unmount      |

### Safety Limits

To prevent unbounded iteration on large entities, use `maxPages` and/or `maxRecords`:

```javascript theme={null}
for await (const record of sdk.paginateRecords('tasks', {
  maxPages: 10,
  maxRecords: 500,
  signal: controller.signal,
})) {
  process(record);
}
```

### Error Handling During Pagination

```javascript theme={null}
try {
  for await (const record of sdk.paginateRecords('tasks')) {
    process(record);
  }
} catch (error) {
  // error.message includes page number and cursor for debugging
  console.error('Pagination failed:', error.message);
}
```

`paginateRecords` and all read methods are allowed during maintenance mode.

## Page Size Limits

| Default page size | Max page size |
| ----------------- | ------------- |
| 25                | 100           |

Requesting more than 100 records per page throws a validation error.
