# Pagination

Read a single page from any list method, or let the SDK walk every page for you.

Every `list` method in `medblocks` is cursor-paginated. Await the call and you get one page back. Call `.autoPagingIterator()` on the same call and the SDK follows the cursor for you, page after page, until there is nothing left.

The two modes share one call. You choose how to consume it.

## Fetch one page [#fetch-one-page]

Await a `list` call to get a single `Page`. It carries the items in `data`, a `has_more` flag, and a `next_cursor` you pass back to get the page after this one.

```ts title="server/first-page.ts"
const page = await mb.patients.list({ limit: 50 });

for (const patient of page.data) {
  console.log(patient.id);
}

if (page.has_more && page.next_cursor) {
  const next = await mb.patients.list({ limit: 50, starting_after: page.next_cursor });
}
```

A `Page` exposes three fields.

| Field         | What it is                                                              |
| ------------- | ----------------------------------------------------------------------- |
| `data`        | The items on this page, typed to the resource you listed.               |
| `has_more`    | `true` when at least one more page exists after this one.               |
| `next_cursor` | The cursor to pass as `starting_after` next time, or `null` at the end. |

`Page` is exported, so you can annotate code that receives one.

```ts title="server/types.ts"
import type { Page, Patient } from "medblocks";

function firstId(page: Page<Patient>) {
  return page.data[0]?.id ?? null;
}
```

Awaiting the same `list` call more than once does not refetch. The SDK caches the first page on the returned value, so two awaits share one network request.

```ts title="server/cached.ts"
const patients = mb.patients.list({ limit: 50 });
const a = await patients;
const b = await patients;
// one request, same page
```

## Page through everything [#page-through-everything]

When you want the whole result set, call `.autoPagingIterator()` instead of awaiting, and drive it with `for await`. The SDK yields each item, then follows `next_cursor` to fetch the next page, and stops when `has_more` is `false`.

```ts title="server/all-patients.ts"
let count = 0;
for await (const patient of mb.patients.list({ limit: 100 }).autoPagingIterator()) {
  count += 1;
}
console.log(`${count} patients total`);
```

It works the same on every list method.

```ts title="server/iterate-any.ts"
for await (const session of mb.patients.listPatientSessions("user_42").autoPagingIterator()) { /* ... */ }
for await (const source of mb.connections.list({ q: "epic" }).autoPagingIterator()) { /* ... */ }
```

Be careful walking the whole catalog. `mb.connections.list()` spans every source Medblocks knows, which is large. Filter it with `q` or `type`, or page through it manually, rather than iterating all of it in one loop.

## Manual cursors [#manual-cursors]

`.autoPagingIterator()` walks everything in a single pass, which suits a script that runs start to finish. Reach for manual cursors when the job needs to survive interruptions or run in batches. Save `next_cursor` after each page and you can resume where you left off, across separate runs of a cron or queue worker.

The cursor is an opaque token. Store it as is and pass it back as `starting_after`. Do not build or parse one yourself.

```ts title="server/walk-with-checkpoint.ts"
let cursor: string | null = await checkpoint.read();

while (true) {
  const page = await mb.patients.list({ limit: 100, starting_after: cursor ?? undefined });
  for (const patient of page.data) await process(patient);

  if (!page.has_more || !page.next_cursor) break;
  cursor = page.next_cursor;
  await checkpoint.write(cursor);
}
```

## Limits [#limits]

Pass `limit` to set the page size. The default is small, so set it explicitly when you page through a lot of data.

| Param            | Default | Range                                         |
| ---------------- | ------- | --------------------------------------------- |
| `limit`          | `10`    | `1` to `100`                                  |
| `starting_after` | none    | A cursor from a previous page’s `next_cursor` |

A `limit` above `100` is rejected with an `invalid_request_error`. Some lists take extra filters on top of these, such as `q` and `type` on `mb.connections.list`. Each resource documents its own.

## See also [#see-also]

<RelatedLinks>
  * [Errors](/docs/reference/errors)
  * [Create patients](/docs/patient-access/create-patients)
</RelatedLinks>

## Need help?

Running into an issue or have a question the docs do not answer? Get support from the Medblocks team through https://forms.gle/dtVxkmfL6Yimp6SBA and we will get back to you.
