uidudevelopers
Recipes

Paginate every page

Walk every page of a Relay connection with the paginate() helper.

Most list* functions in @uidu/client return the first page only — they're tuned for the common case of "show the first N." When you need everything (a sitemap, generateStaticParams, an export), use the paginate() helper.

What it does

paginate() repeatedly calls a list query, threading pageInfo.endCursor into the next call's after argument until hasNextPage is false. Returns a flat array of nodes.

Signature

import { paginate, ListEventsDocument } from '@uidu/client';

const all = await paginate(client, ListEventsDocument, {
  variables: { first: 100 },
  extract: (d) => d.currentWorkspace?.events,
});
ArgumentTypeNotes
clientUiduClientCreated with createClient.
documentTypedDocumentNodeAny List*Document re-exported from @uidu/client.
variablesOmit<Vars, 'after'>First-page variables. after is set by paginate itself.
extract(data) => RelayConnection | nullPluck the connection out of the result shape.
maxPagesnumber (default 1000)Safety bound — guards against runaway loops.

Sitemap with every page

// app/sitemap.ts
import { paginate, ListPagesDocument } from '@uidu/client';
import { client } from '@/lib/uidu';

export default async function sitemap() {
  const pages = await paginate(client, ListPagesDocument, {
    variables: { projectId: process.env.UIDU_PROJECT_ID!, first: 100 },
    extract: (d) => d.currentWorkspace?.project?.pages,
  });

  return pages.map((p) => ({
    url: `https://example.com/${p.slug}`,
    lastModified: p.updatedAt,
  }));
}

generateStaticParams for every event

// app/events/[id]/page.tsx
import { paginate, ListEventsDocument } from '@uidu/client';
import { client } from '@/lib/uidu';

export async function generateStaticParams() {
  const events = await paginate(client, ListEventsDocument, {
    variables: { first: 100 },
    extract: (d) => d.currentWorkspace?.events,
  });
  return events.map((e) => ({ id: e.id }));
}

Other List Documents

The same pattern works with any of the re-exported Documents:

ListBookingsDocument, ListCalendarsDocument, ListCalendarEventsDocument, ListCallsDocument, ListCampaignsDocument, ListEmailCampaignsDocument, ListCoursesDocument, ListDealsDocument, ListDonationCampaignsDocument, ListEmployeesDocument, ListEnrollmentsDocument, ListEventsDocument, ListFormsDocument, ListKbArticlesDocument, ListKbCollectionsDocument, ListLecturesDocument, ListPagesDocument, ListStoriesDocument.

Why a Document, not the list function?

The user-facing listEvents(client, vars) returns a flat array — it throws away pageInfo to keep the common case ergonomic. paginate() needs pageInfo, so it works one level lower against the raw TypedDocumentNode.

Be mindful with very large collections. paginate() is memory-bound — it holds the full result in RAM. For exports of 10k+ records, prefer streaming the data straight to disk or a downstream sink instead.

On this page