MarzbanSDK
Utilities

Pagination

Walk every page of a paginated endpoint without hand-writing the offset/limit loop yourself.

Endpoints like sdk.user.getUsers are paginated with offset/limit, and stop reporting more once you've paged through everything. paginateAll wraps that loop into an async generator, so you write the fetch call once and consume items one at a time.

Import

import { paginateAll } from 'marzban-sdk'

paginateAll(fetchPage, opts?)

fetchPage(offset, limit) is your own call to a paginated method, adapted to return { items, total? }paginateAll knows nothing about any specific endpoint.

for await (const user of paginateAll((offset, limit) =>
  sdk.user.getUsers({ offset, limit }).then(r => ({ items: r.users, total: r.total }))
)) {
  console.log(user.username)
}
OptionTypeDefaultDescription
pageSizenumber100Items requested per page

total is optional in the { items, total? } shape because not every paginated endpoint reports one — getUsers does, but getAdmins/getUserTemplates return a bare array with no count of the whole collection. Omit total for those; paginateAll falls back to stopping once a page comes back shorter than pageSize (or empty) instead.

Collecting everything into an array

paginateAll yields one item at a time — spread it or push into an array if you want them all at once:

const admins = []
for await (const admin of paginateAll(
  (offset, limit) => sdk.admin.getAdmins({ offset, limit }).then(items => ({ items })),
  { pageSize: 50 }
)) {
  admins.push(admin)
}

Stopping early

Since it's a generator, break out of the loop stops fetching further pages — the next page is never requested:

for await (const user of paginateAll((offset, limit) =>
  sdk.user.getUsers({ offset, limit }).then(r => ({ items: r.users, total: r.total }))
)) {
  if (user.status === 'expired') {
    console.log('First expired user:', user.username)
    break
  }
}

On this page