Client Methods
Rule
Browser/client code imports named methods, hooks, or client modules instead of
hand-writing REST calls to framework or app routes.
Why
Route shapes are transport details. If components and docs call
fetch("/_agent-native/...") or template /api/* routes directly, every caller
has to rediscover auth, base paths, request-source headers, JSON parsing, error
handling, optimistic updates, sync invalidation, and route quirks. A named client
method gives the UI, docs, and future agents one stable contract.
How
Look for an existing client API first.
| Need |
Use |
| App action reads/writes |
useActionQuery / useActionMutation from @agent-native/core/client/hooks |
| Imperative action calls |
callAction from @agent-native/core/client/hooks |
| Browser application state |
readClientAppState, writeClientAppState, setClientAppState, deleteClientAppState from @agent-native/core/client/hooks |
| Navigation/app-state sync |
useAgentRouteState / useSemanticNavigationState from @agent-native/core/client/navigation |
| Agent chat context |
Agent chat helpers from @agent-native/core/client/agent-chat |
| Ask the user a multiple-choice question from app code |
askUserQuestion from @agent-native/core/client/agent-chat (renders inline in the agent panel; answer goes to the agent — do not build a custom modal) |
| Live sync |
useDbSync, useChangeVersion, useChangeVersions from @agent-native/core/client/hooks |
| Extension iframe calls |
appAction, appFetch, extensionFetch from the extension runtime |
Action fetch behavior: every useActionQuery / useActionMutation /
callAction request is bounded by a 60s timeout, and timeouts surface as
errors instead of retrying silently. useActionQuery cancels superseded
requests automatically via React Query's abort signal. For imperative
calls, callAction(name, params, { method, signal, timeoutMs }) accepts an
AbortSignal and a timeoutMs override for legitimately long operations.
If no client API exists, add the narrowest helper at the boundary.
- Put shared framework helpers in
packages/core/src/client/*.
- Put template-local helpers in
templates/<app>/app/hooks/*,
templates/<app>/app/lib/*, or an existing local client module.
- Export reusable core helpers from the focused
@agent-native/core/client/*
entry for their domain. Never recommend the deprecated broad
@agent-native/core/client barrel in new code.
- Keep raw
fetch, agentNativePath, and route paths inside that helper,
not scattered through components or docs.
- Add focused tests for URL construction, headers, response parsing, error
shape, and any sync invalidation.
Teach the helper, not the route.
Docs, skills, examples, and generated code should show:
await setClientAppState("selection", selection, { keepalive: true });
not:
await fetch("/_agent-native/application-state/selection", {
method: "PUT",
body: JSON.stringify(selection),
});
Exceptions
Raw route calls are acceptable only inside low-level client helpers or for
route-shaped protocols that cannot be hidden cleanly:
- multipart uploads
- streaming/SSE/WebSocket transports
- OAuth redirects and callback URL construction
- webhooks and external provider callbacks
- extension sandbox
appFetch / extensionFetch, which are themselves exposed
client methods
- tests that assert route construction
Even for exceptions, prefer a named helper as soon as more than one caller needs
the behavior.
Don't
- Don't put
fetch("/_agent-native/..."), fetch(agentNativePath(...)), or
template /api/* calls directly in React components for normal app data,
actions, settings, or application state.
- Don't document route calls as the way client code should do work.
- Don't add pass-through
/api/* routes just to make client fetches look
simpler; expose an action and call it with action hooks.
- Don't duplicate auth/session/base-path/request-source/error parsing logic in
every component.
Related Skills
actions — app operations shared by UI and agent.
context-awareness — application-state navigation and selection helpers.
real-time-sync — keeping helper-backed UI reads fresh.
server-plugins — when a new route is actually warranted.
References
references/legacy-client-fetch-audit-2026-06-03.md — known legacy cleanup
targets found when this rule was added.
1---2name: client-methods3description: Client method surface rules. Use when wiring browser/client code to actions, application state, framework routes, app APIs, uploads, auth, or settings.4---56# Client Methods78## Rule910Browser/client code imports named methods, hooks, or client modules instead of11hand-writing REST calls to framework or app routes.1213## Why1415Route shapes are transport details. If components and docs call16`fetch("/_agent-native/...")` or template `/api/*` routes directly, every caller17has to rediscover auth, base paths, request-source headers, JSON parsing, error18handling, optimistic updates, sync invalidation, and route quirks. A named client19method gives the UI, docs, and future agents one stable contract.2021## How22231. Look for an existing client API first.2425 | Need | Use |26 | --- | --- |27 | App action reads/writes | `useActionQuery` / `useActionMutation` from `@agent-native/core/client/hooks` |28 | Imperative action calls | `callAction` from `@agent-native/core/client/hooks` |29 | Browser application state | `readClientAppState`, `writeClientAppState`, `setClientAppState`, `deleteClientAppState` from `@agent-native/core/client/hooks` |30 | Navigation/app-state sync | `useAgentRouteState` / `useSemanticNavigationState` from `@agent-native/core/client/navigation` |31 | Agent chat context | Agent chat helpers from `@agent-native/core/client/agent-chat` |32 | Ask the user a multiple-choice question from app code | `askUserQuestion` from `@agent-native/core/client/agent-chat` (renders inline in the agent panel; answer goes to the agent — do not build a custom modal) |33 | Live sync | `useDbSync`, `useChangeVersion`, `useChangeVersions` from `@agent-native/core/client/hooks` |34 | Extension iframe calls | `appAction`, `appFetch`, `extensionFetch` from the extension runtime |3536 Action fetch behavior: every `useActionQuery` / `useActionMutation` /37 `callAction` request is bounded by a 60s timeout, and timeouts surface as38 errors instead of retrying silently. `useActionQuery` cancels superseded39 requests automatically via React Query's abort signal. For imperative40 calls, `callAction(name, params, { method, signal, timeoutMs })` accepts an41 `AbortSignal` and a `timeoutMs` override for legitimately long operations.42432. If no client API exists, add the narrowest helper at the boundary.4445 - Put shared framework helpers in `packages/core/src/client/*`.46 - Put template-local helpers in `templates/<app>/app/hooks/*`,47 `templates/<app>/app/lib/*`, or an existing local client module.48 - Export reusable core helpers from the focused `@agent-native/core/client/*`49 entry for their domain. Never recommend the deprecated broad50 `@agent-native/core/client` barrel in new code.51 - Keep raw `fetch`, `agentNativePath`, and route paths inside that helper,52 not scattered through components or docs.53 - Add focused tests for URL construction, headers, response parsing, error54 shape, and any sync invalidation.55563. Teach the helper, not the route.5758 Docs, skills, examples, and generated code should show:5960 ```ts61 await setClientAppState("selection", selection, { keepalive: true });62 ```6364 not:6566 ```ts67 await fetch("/_agent-native/application-state/selection", {68 method: "PUT",69 body: JSON.stringify(selection),70 });71 ```7273## Exceptions7475Raw route calls are acceptable only inside low-level client helpers or for76route-shaped protocols that cannot be hidden cleanly:7778- multipart uploads79- streaming/SSE/WebSocket transports80- OAuth redirects and callback URL construction81- webhooks and external provider callbacks82- extension sandbox `appFetch` / `extensionFetch`, which are themselves exposed83 client methods84- tests that assert route construction8586Even for exceptions, prefer a named helper as soon as more than one caller needs87the behavior.8889## Don't9091- Don't put `fetch("/_agent-native/...")`, `fetch(agentNativePath(...))`, or92 template `/api/*` calls directly in React components for normal app data,93 actions, settings, or application state.94- Don't document route calls as the way client code should do work.95- Don't add pass-through `/api/*` routes just to make client fetches look96 simpler; expose an action and call it with action hooks.97- Don't duplicate auth/session/base-path/request-source/error parsing logic in98 every component.99100## Related Skills101102- `actions` — app operations shared by UI and agent.103- `context-awareness` — application-state navigation and selection helpers.104- `real-time-sync` — keeping helper-backed UI reads fresh.105- `server-plugins` — when a new route is actually warranted.106107## References108109- `references/legacy-client-fetch-audit-2026-06-03.md` — known legacy cleanup110 targets found when this rule was added.