# Pagination & field values

URL: https://developers.uidu.org/docs/foundations/pagination-and-field-values

> Reading whole collections — list functions, paginate, useQuery — and the one shape every field value's content takes.

Two rules that apply to every domain.

## Lists are Relay connections

Every list in the GraphQL API is a Relay connection: `{ edges: [{ node }], pageInfo: { hasNextPage, endCursor } }`.
How much of that you see depends on how you read it:

| You call                                      | You get                              | Use it for                                |
| --------------------------------------------- | ------------------------------------ | ----------------------------------------- |
| `listEvents(client, { first: 20 })`           | one page, **flattened** to `Event[]` | "the latest 20" — most pages              |
| `paginate(client, ListEventsDocument, …)`     | **every** page, flattened            | sitemaps, `generateStaticParams`, exports |
| `useQuery(ListEventsDocument, { first: 20 })` | one page, **raw** — `edges[].node`   | Client Components                         |

### One page — `list*`

`list*` functions take `first` (page size) and, where the query supports it, `after` (a cursor) plus
domain filters. They return the nodes only, so they can't tell you whether there is a next page — when
you need that, use `paginate` or the raw connection.

```ts
import { listStories } from '@uidu/client';

const latest = await listStories(client, { first: 10 });
```

### Everything — `paginate`

`paginate` walks the connection for you, threading `endCursor` into `after` until `hasNextPage` is
false, and returns one flat array. Pass the generated `*Document` for the list and tell it where the
connection sits in the result:

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

const events = await paginate(client, ListEventsDocument, {
  variables: { first: 100 },
  extract: (data) => data.currentWorkspace?.events,
});
```

`maxPages` (default 1000) bounds the loop. Every list document is exported from `@uidu/client` — the
[client reference](https://developers.uidu.org/docs/reference/client.md) lists them per domain. Worked examples: [Paginate every
page](https://developers.uidu.org/docs/recipes/paginate.md), [Dynamic CMS routes](https://developers.uidu.org/docs/recipes/dynamic-cms-routes.md).

### In a Client Component — `useQuery`

[`useQuery`](https://developers.uidu.org/docs/tools/react/hooks.md) runs the same documents through SWR and returns what the query
returns, connection included:

```tsx
const { data } = useQuery(ListEventsDocument, { first: 20 });
const events = data?.currentWorkspace?.events?.edges?.map((e) => e?.node) ?? [];
const more = data?.currentWorkspace?.events?.pageInfo?.hasNextPage;
```

## A field value's `content` is always `{ value: … }`

Field values carry the data of CMS blocks, form responses, Model items, and the custom fields of
contacts, bookings or stories. Whenever you **write** one, its `content` is an object with a `value`
key — never the bare value:

```ts
// ✅ every value wrapped
fieldValuesAttributes: [
  { fieldId: 'f1', content: { value: 'hello@example.com' } },
  { fieldId: 'f2', content: { value: 42 } },
  { fieldId: 'f3', content: { value: ['gid://…', 'gid://…'] } }, // multipleSelect
];

// ❌ silently loses the answer
fieldValuesAttributes: [{ fieldId: 'f1', content: 'hello@example.com' }];
```

It holds for every field kind (`{ value: true }`, `{ value: '2026-07-27' }`, `{ value: <doc> }` for
rich text) and wherever `fieldValuesAttributes` appears.

<Callout type="warn" title="A flat value fails quietly">
  The API doesn't reject a bare value: the record saves, its typed columns stay empty, and reading that
  field later errors. `@uidu/client` wraps a bare `content` for you on every mutation and warns in
  development — but other API clients won't, so send the right shape.
</Callout>

If you assemble `fieldValuesAttributes` by hand outside an SDK mutation, `normalizeFieldValuesAttributes`
(or `normalizeFieldValueContent` for one value) applies the same fix. For Model items,
`toFieldValuesAttributes(model, { room: 'Blu' })` builds the array from shortnames — see
[Models](https://developers.uidu.org/docs/domains/models.md).

Reading is simpler: [`useFields`](https://developers.uidu.org/docs/tools/react/hooks.md) turns a CMS block's `fieldValues` into a
`{ shortname: value }` map, and Model items come with `fieldValuesByShortname`.
