Skip to content

Core Query Usage

Use generated query proxies to retrieve data in React with strong typing and predictable state.

Arc generates query proxies from backend queries (controller-based and model-bound). These proxies expose static React helpers such as .use() and .useWithPaging().

See Backend Proxy Generation for setup details.

From a React component, call the generated static use() method:

export const MyComponent = () => {
const [accounts, queryAccounts] = AllAccounts.use();
return (
<>
</>
);
};

All results are strongly typed from backend metadata.

Use the tuple shape emitted for your backend result. Omitting trailing elements while destructuring is fine; treating the tuple itself as the model is not.

Proxy shape.use() / .useSuspense().useWithPaging() / .useSuspenseWithPaging()
Ordinary single or enumerable[result, perform, setSorting]Enumerable only: [result, perform, setSorting, setPage, setPageSize]
Observable enumerable[result, setSorting][result, setSorting, setPage, setPageSize]
Observable single[result]Not generated

Parameterized proxies emit NameParameters, not INameParameters. Their calls take args first and, for enumerables, optional sorting next. Parameterless enumerable calls take sorting first; parameterless single-result calls take no arguments. Paging prepends pageSize to these argument positions. For example, BooksForAuthor.use({ authorId }) passes a named argument object, whereas parameterless AllBooks.use(sorting) does not take an undefined argument placeholder.

Observable queries differ and are covered in Observable Queries.

The query result type in React is QueryResultWithState. It implements the same IQueryResult contract as QueryResult and adds React-focused execution state.

Properties from the shared IQueryResult contract:

PropertyDescription
dataThe actual data returned in the expected type.
isSuccessWhether the query completed successfully.
isReadyWhether the query has produced a result. false is transient, not a failure.
isAuthorizedWhether the query was authorized.
isValidWhether the query input was valid.
validationResultsValidation errors returned by the backend.
hasExceptionsWhether exceptions were returned.
exceptionMessagesException messages from the backend.
exceptionStackTraceException stack trace when available.
pagingPaging metadata (page, size, totals) when paging is enabled.

React execution state:

PropertyDescription
isPerformingWhether the query is currently fetching data.

An observable query HTTP request can return 202 Accepted with isReady: false before the observable has produced its first result. This is a transient server state: isSuccess is false, but hasExceptions remains false. Inspect isReady before treating an unsuccessful result as a failure.

isReady and isPerforming describe different things. Readiness says whether a result has been produced; performing says whether the client is currently fetching or subscribing. Initial React query state is not ready, while disabled or synthetic failure states are ready because no server result remains pending.

IQueryResult.isReady is optional for source compatibility: older Arc servers and third-party structural query-result objects may omit it. Arc normalizes query results received through its HTTP, WebSocket, and Server-Sent Events transports to isReady: true when the member is absent, while preserving an explicit false. SDK-produced QueryResult and QueryResultWithState instances always expose a concrete boolean.

Queries can expose parameters on the backend — in C# through attributes such as [FromQuery] and route parameters, on the JVM through ordinary query-method parameters, and in TypeScript through @query(argument(...)) on a static read-model method. These become typed arguments on generated proxies.

[HttpGet("starting-with")]
public IEnumerable<DebitAccount> StartingWith([FromQuery] string? filter)
{
var filterDocument = Builders<DebitAccount>
.Filter
.Regex("name", $"^{filter ?? string.Empty}.*");
return _collection.Find(filterDocument).ToList();
}

View C# snippet source on GitHub

The C# tab shows a controller action, because that is the style this attribute-driven parameter binding belongs to. The JVM has no controller-based discovery, so its parameters are simply the query method’s own, declared on the @ReadModel’s companion object. The TypeScript tab declares its argument explicitly with argument('filter', String, { optional: true }). All tabs use the shared frontend query runtime; their generated imports and paths depend on each backend’s configuration.

Use the parameterized proxy from React:

export const MyComponent = () => {
const [accounts, queryAccounts] = StartingWith.use({ filter: '' });
return (
<>
</>
);
};

An enabled hook fetches again whenever any argument changes — optional arguments such as filter as well as required ones. Arguments are compared by their serialized value, so passing a new object with the same values does not trigger a request. Each set of arguments also gets its own cached result; see query instance caching.

Ordinary query fetches use httpHeadersCallback. Native EventSource/WebSocket handshakes cannot attach those arbitrary headers; SSE control POSTs are separate fetch requests. Cookies are browser-managed, not configurable Cookie request headers. See the transport credentials matrix.