# How to Implement Frontend Data Fetching with useActionQuery and useActionMutation Hooks in Agent-Native

> Learn frontend data fetching in Agent-Native using useActionQuery and useActionMutation hooks. Simplify REST calls with typed actions, caching, and retries via React Query.

- Repository: [Builder.io/agent-native](https://github.com/BuilderIO/agent-native)
- Tags: how-to-guide
- Published: 2026-07-02

---

**Agent-Native provides `useActionQuery` and `useActionMutation` hooks in `@agent-native/core/client` that replace manual REST calls with typed, action-driven data fetching, automatically handling caching, retries, and query invalidation through React Query integration.**

Agent-Native replaces traditional REST API calls with a type-safe, action-driven transport layer. The framework exposes two primary React hooks—`useActionQuery` for idempotent read operations and `useActionMutation` for state-changing mutations—that abstract HTTP complexity while providing end-to-end TypeScript inference via the `AgentNativeActionRegistry`.

## Core Architecture and Transport Layer

The implementation resides in **[`packages/core/src/client/use-action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/use-action.ts)**, which exports three key utilities: `useActionQuery`, `useActionMutation`, and the imperative `callAction` helper. These functions wrap a central transport mechanism called `actionFetch` (defined around lines 38-53) that constructs requests to the framework endpoint `/_agent-native/actions/<action-name>`.

Every request automatically receives two critical headers:

- **`X-Agent-Native-Frontend: 1`** — Identifies the caller as a UI component, distinguishing browser requests from server-side or external API calls.
- **`x-user-timezone`** — Propagates the browser’s IANA timezone to ensure server-side "today" logic aligns with the client’s local time.

Both hooks integrate with **React Query** (`@tanstack/react-query`) to provide automatic retries (excluding authentication failures), request deduplication, and background refetching. According to the BuilderIO/agent-native source code, mutations automatically invalidate cached queries sharing the `"action"` query key, triggering immediate UI updates.

## Fetching Data with useActionQuery

**`useActionQuery`** handles idempotent GET operations such as listing, reading, or searching data. The hook returns a standard React Query result containing `data`, `isLoading`, `error`, and `refetch` properties, with TypeScript types inferred directly from your server-side action definitions.

### Basic List Query

Consume a server-side action without manual URL construction:

```typescript
import { useActionQuery } from "@agent-native/core/client";

export function FormList() {
  // TypeScript infers the return type (e.g., Form[]) from AgentNativeActionRegistry
  const { data: forms, isLoading, error } = useActionQuery("list-forms");

  if (isLoading) return <p>Loading…</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <ul>
      {forms?.map((f) => (
        <li key={f.id}>{f.title}</li>
      ))}
    </ul>
  );
}

```

### Parameterized Queries

Pass filter parameters as a second argument. The hook uses `serializeActionQueryParams` to convert the object into a query string, handling arrays via bracket syntax through `appendActionQueryParam`:

```typescript
import { useActionQuery } from "@agent-native/core/client";

export function PublishedForms() {
  const { data, isLoading } = useActionQuery("list-forms", {
    status: "published", // Becomes ?status=published
    limit: 10,
  });

  if (isLoading) return <div>Loading forms…</div>;
  
  return <FormGrid data={data} />;
}

```

## Mutating Data with useActionMutation

**`useActionMutation`** manages state-changing operations (POST, PUT, DELETE). Defined around lines 126-155 in [`use-action.ts`](https://github.com/BuilderIO/agent-native/blob/main/use-action.ts), this hook wraps React Query’s `useMutation` and automatically invalidates related queries upon success by calling `queryClient.invalidateQueries({ queryKey: ["action"] })`.

### Creating Records

```typescript
import { useActionMutation, useActionQuery } from "@agent-native/core/client";

export function NewForm() {
  const { mutate: createForm, isLoading } = useActionMutation("create-form");
  const { data: forms, refetch } = useActionQuery("list-forms");

  const handleSubmit = async (title: string) => {
    await createForm({ title }); // POST to /_agent-native/actions/create-form
    
    // Explicit refetch (or rely on automatic invalidation)
    await refetch();
  };

  return (
    <form onSubmit={(e) => handleSubmit(e.target.title.value)}>
      <input name="title" />
      <button disabled={isLoading}>Create</button>
    </form>
  );
}

```

### Custom HTTP Methods

Override the default POST method by passing a `method` option directly to `actionFetch`:

```typescript
import { useActionMutation } from "@agent-native/core/client";

export function DeleteForm({ id }: { id: string }) {
  const { mutate: deleteForm, isPending } = useActionMutation("delete-form", {
    method: "DELETE",
  });

  return (
    <button 
      onClick={() => deleteForm({ id })} 
      disabled={isPending}
    >
      Delete Form
    </button>
  );
}

```

## Type Safety and the Action Registry

Agent-Native achieves end-to-end type safety through the **`AgentNativeActionRegistry`**. When you define actions on the server using `defineAction`, the Vite plugin generates declaration files (typically in [`packages/core/.generated/action-types.d.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/.generated/action-types.d.ts)) that extend this registry with the action’s specific `params` and `result` types.

As implemented in BuilderIO/agent-native, this means:

- **Parameter autocompletion**: The second argument to `useActionQuery` or the first argument to the mutation function knows the exact shape required by your server definition.
- **Return type inference**: The `data` property is automatically typed based on the action’s declared return value.
- **Compile-time validation**: Mismatched parameters trigger TypeScript errors before build time.

## Imperative Calls Outside Components

For scenarios where React hooks are not ergonomic—such as debounced search inputs or server-side rendering—use the **`callAction`** helper:

```typescript
import { callAction } from "@agent-native/core/client";

async function prefetchForm(id: string) {
  // Returns Promise<Form> based on registry types
  const form = await callAction("get-form", { id });
  return form;
}

```

This low-level function bypasses React Query’s caching layer but maintains the same transport logic, headers, and type safety as the hook-based alternatives.

## Summary

- **File location**: Core implementation lives in [`packages/core/src/client/use-action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/use-action.ts), with path construction handled in [`packages/core/src/client/api-path.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/api-path.ts).
- **useActionQuery**: Wraps React Query’s `useQuery` for idempotent GET requests to `/_agent-native/actions/<action-name>`, with automatic header injection and timezone propagation.
- **useActionMutation**: Wraps `useMutation` for state changes, automatically invalidating cached `"action"` queries on success to keep UI synchronized.
- **Type safety**: Full TypeScript inference via `AgentNativeActionRegistry`, populated by the build-time Vite plugin based on server-side action definitions.
- **Transport**: Central `actionFetch` utility handles serialization, error parsing, and header management consistently across all hooks.

## Frequently Asked Questions

### What is the difference between useActionQuery and useActionMutation?

**`useActionQuery`** is designed for idempotent read operations (GET requests) and integrates with React Query’s caching, background refetching, and deduplication mechanisms. **`useActionMutation`** handles state-changing operations (POST, PUT, DELETE) and automatically invalidates cached queries after successful execution to ensure data consistency across your application.

### How does Agent-Native maintain TypeScript type safety across the client-server boundary?

The framework uses the **`AgentNativeActionRegistry`** interface, which is augmented at build time by a Vite plugin that scans your server-side `defineAction` calls. When you import from `@agent-native/core/client`, the hooks use this registry to infer parameter shapes and return types, providing autocompletion and compile-time validation without manual type definitions.

### What HTTP headers does Agent-Native automatically add to requests?

As implemented in the `actionFetch` function within [`packages/core/src/client/use-action.ts`](https://github.com/BuilderIO/agent-native/blob/main/packages/core/src/client/use-action.ts), every request automatically includes **`X-Agent-Native-Frontend: 1`** to identify browser-based callers and **`x-user-timezone`** containing the browser’s IANA timezone identifier. These headers enable server-side logic to differentiate request sources and align temporal calculations with the user’s local time.

### How do I manually invalidate cached queries after a mutation?

While `useActionMutation` automatically invalidates queries with the key `["action"]`, you can manually trigger invalidation using React Query’s query client. Import `queryClient` and call `queryClient.invalidateQueries({ queryKey: ["action"] })` to force refetching of all action-based queries, or specify a more specific query key to target individual action results.