Queries & commands

A page calls a Frond with a query for reads or a command for writes. A call is identified by the entity class and operation name. The class carries the metadata and its name is used as the registration key.

import Post from '@frond/blog/entities/Post';

const { items, total, hasMore, loading, error } = await useQuery<PostCard>(Post, 'list');
const publish = useCommand<Post>(Post, 'publish');
await publish.execute({ params: { id } });
// → every mounted query on Post revalidates, automatically

useQuery

useQuery<T>(Entity, op, input?, opts?)
// input: CallInput | () => CallInput      CallInput = { params?, query?, body? }
// opts:  { immediate?: boolean }          immediate: false → don't fire on mount

Returns (all reactive):

KeyTypeNotes
dataRef<T | null>the raw result (single object ops: findById, bySlug…)
itemsComputedRef<T[]>list normalization: a bare array or a ListResult both read as items
total, hasMorefrom ListResultpresent when the op returns one
loadingRef<boolean>
errorRef<FougereError | null>the revived typed error (Errors)
refresh() => Promise<void>manual revalidation — rarely needed, see the link

A getter input makes the query reactive to its arguments:

const { items } = await useQuery<Hit>(Post, 'searchByTitle',
  () => ({ body: { title: submitted.value } }),
  { immediate: false });

useCommand

useCommand<T>(Entity, op)
// → { execute(input?): Promise<T>, loading: Ref<boolean>, error: Ref<FougereError | null> }

execute rejects with the same FougereError it stores in error — await it in a try/catch or read the ref, either style works.

A successful command on an entity revalidates active queries for that entity. After a post is published, post lists are therefore refreshed. Clearing this cache does not change operation results, but causes new reads.

The wire — and SSR

The browser POSTs the same JSON-RPC frame process-to-process transport uses (POST /_fougere/call, method: "post.list", params = the invocation).

The documentation calls the POST /_fougere/call endpoint used by useQuery, useCommand, and invoke the envelope. The REST and GraphQL surfaces translate their protocols to the same operations.

Two properties:

  • During SSR, the same call collapses to in-process execution — no network, no port.
  • The browser does not provide state: the server builds it from the request context, including the session.

The client transport does not automatically retry commands and preserves typed errors (Errors).

Next: Forms — state, validation, and submission.

Built with Fougere — this site runs on the framework it documents.