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

# Query

> API reference for query() — data fetching and caching

`query()` creates a reactive data-fetching primitive. Pass it a SDK method call and it returns reactive properties (`data`, `loading`, `error`) that auto-update in the DOM. It supports SSR data pre-fetching and integrates with the entity store for cross-component cache invalidation.

## query()

Pass a generated SDK method call. The cache key and types are inferred automatically from the SDK:

```tsx theme={null}
import { query } from '@vertz/ui/query';

function TaskList() {
  const tasks = query(api.tasks.list());

  return (
    <div>
      {tasks.loading && <p>Loading...</p>}
      {tasks.error && <p>Error: {tasks.error.message}</p>}
      {tasks.data?.items.map((task) => (
        <div key={task.id}>{task.title}</div>
      ))}
    </div>
  );
}
```

SDK methods return a `QueryDescriptor` — an object that carries the fetch function, a deterministic cache key, and entity metadata. `query()` unwraps the `Result` for you: if the SDK call succeeds, `data` gets the value; if it fails, `error` gets the error. See the [Generated SDK](/api-reference/fetch/sdk) reference for details on how SDK methods and the `Result` type work.

### Signature

```ts theme={null}
function query<T, E>(
  descriptor: QueryDescriptor<T, E>,
  options?: Omit<QueryOptions<T>, 'key'>,
): QueryResult<T, E>;
```

***

## QueryResult

The returned object has reactive properties that auto-update in the DOM — the compiler handles reactivity for you.

| Property       | Type             | Description                                                          |
| -------------- | ---------------- | -------------------------------------------------------------------- |
| `data`         | `T \| undefined` | Resolved data, or `undefined` while loading                          |
| `loading`      | `boolean`        | `true` during the initial fetch                                      |
| `revalidating` | `boolean`        | `true` during background refetch (stale-while-revalidate)            |
| `error`        | `E \| undefined` | Error from the last failed fetch                                     |
| `idle`         | `boolean`        | `true` when the query has never fetched (e.g. thunk returned `null`) |
| `refetch()`    | `() => void`     | Re-execute the query (shows loading state)                           |
| `revalidate()` | `() => void`     | Background refetch (keeps current data visible)                      |
| `dispose()`    | `() => void`     | Cancel the query and clean up subscriptions                          |

<Note>
  `refetch()` resets `data` to `undefined` and shows loading state. `revalidate()` keeps the current
  data visible while fetching fresh data in the background (stale-while-revalidate pattern).
</Note>

***

## QueryOptions

| Option            | Type                                                      | Default | Description                                                 |
| ----------------- | --------------------------------------------------------- | ------- | ----------------------------------------------------------- |
| `key`             | `string`                                                  | —       | Cache key (auto-inferred from SDK descriptors)              |
| `initialData`     | `T`                                                       | —       | Initial data to use before the first fetch completes        |
| `debounce`        | `number`                                                  | —       | Debounce interval in ms before executing                    |
| `cache`           | `CacheStore<T>`                                           | —       | Custom cache store                                          |
| `ssrTimeout`      | `number`                                                  | —       | Max time to wait for the query during SSR (ms)              |
| `refetchInterval` | `number \| false \| (data, iteration) => number \| false` | `false` | Polling interval in ms, or a function for dynamic intervals |

***

## invalidate()

Force all queries with a matching key to refetch.

```ts theme={null}
import { invalidate } from '@vertz/ui/query';

// After a mutation succeeds, invalidate the list
const result = await api.tasks.create({ title: 'New task' });
if (result.ok) {
  invalidate('task-list');
}
```

### Signature

```ts theme={null}
function invalidate(key: string): void;
```

***

## SSR support

Queries execute during SSR automatically. The server waits for queries to resolve (up to `ssrTimeout`), serializes the data, and the client hydrates without re-fetching.

```tsx theme={null}
const tasks = query(api.tasks.list(), { ssrTimeout: 500 });
// Server: waits up to 500ms for data, injects into HTML
// Client: hydrates with server data, no loading flash
```

***

## Types

```ts theme={null}
interface QueryOptions<T> {
  initialData?: T;
  debounce?: number;
  key?: string;
  cache?: CacheStore<T>;
  ssrTimeout?: number;
  refetchInterval?: number | false | ((data: T | undefined, iteration: number) => number | false);
}

interface QueryResult<T, E = unknown> {
  readonly data: T | undefined;
  readonly loading: boolean;
  readonly revalidating: boolean;
  readonly error: E | undefined;
  readonly idle: boolean;
  refetch: () => void;
  revalidate: () => void;
  dispose: () => void;
}

interface CacheStore<T> {
  get(key: string): T | undefined;
  set(key: string, value: T): void;
  has(key: string): boolean;
  clear(): void;
}
```
