---
title: Consume generated proxies
editUrl: https://github.com/Cratis/Arc/edit/main/Documentation/frontend/react/proxy-generation.mdx
description: Use backend-generated command and query contracts from React without a hand-written API client.
---

import { Tabs, TabItem } from '@astrojs/starlight/components';


A backend change should produce a compiler error where your frontend needs updating, not a surprise in production. Arc generates TypeScript command, query, and model types from discovered endpoints — for the shared frontend runtime. Run the generator for your backend, then update your consumers; never edit generated files.

You get completion for command fields and query arguments, typed responses, and compiler-guided repairs after a backend rename. The generated modules also give you concrete contracts to navigate instead of hand-maintained request shapes.

## Set up the frontend

Current generated command and query modules import both `@cratis/arc` and `@cratis/arc.react`, even when you call them imperatively. Install their dependencies in your React app:

```bash
npm install @cratis/fundamentals @cratis/arc @cratis/arc.react react react-dom
```

Mount [Arc](/arc/frontend/react/arc/) above hook consumers. For build configuration, namespace mapping, source-file grouping, and output ownership, use the C# [backend proxy-generation reference](/arc/backend/csharp/proxy-generation/), the JVM [TypeScript proxies guide](/arc/backend/kotlin/guides/typescript-proxies/), or the TypeScript source preview's [generator setup](/arc/backend/typescript/proxy-generation/getting-started/).

## Commands use your application services

Arc is a standalone CQRS framework. A command can call an ordinary service and return a response; it does not need events or an event log.

This backend fragment assumes your application implements and registers the account service. That interface is application-owned, not an Arc API:

<Tabs syncKey="arc-backend-language">
<TabItem label="C#">

```csharp
using Cratis.Arc.Commands.ModelBound;
using Cratis.Concepts;

public record AccountName(string Value) : ConceptAs<string>(Value)
{
    public static readonly AccountName NotSet = new(string.Empty);
}

public record AccountBalance(decimal Value) : ConceptAs<decimal>(Value)
{
    public static readonly AccountBalance NotSet = new(0m);
}

public interface IAccountService
{
    Task<Guid> Open(AccountName name, AccountBalance initialBalance);
}

[Command]
public record OpenDebitAccount(AccountName Name, AccountBalance InitialBalance)
{
    public Task<Guid> Handle(IAccountService accounts) =>
        accounts.Open(Name, InitialBalance);
}
```

[View C# snippet source on GitHub](https://github.com/Cratis/Arc/blob/main/Documentation/client-snippets/frontend/react/proxy-generation/open-debit-account.md)

</TabItem>
<TabItem label="Java">

```java
import io.cratis.arc.concepts.ConceptAs;
import java.math.BigDecimal;
import java.util.UUID;

public record AccountName(String value) implements ConceptAs<String> {
}

public record AccountBalance(BigDecimal value) implements ConceptAs<BigDecimal> {
}

public interface AccountService {
    UUID open(AccountName name, AccountBalance initialBalance);
}

@Command
public record OpenDebitAccount(AccountName name, AccountBalance initialBalance) {
    public UUID handle(@FromServices AccountService accounts) {
        return accounts.open(name, initialBalance);
    }
}
```

[View Java snippet source on GitHub](https://github.com/Cratis/Arc.Kotlin/blob/main/Documentation/client-snippets-java/frontend/react/proxy-generation/open-debit-account.md)

</TabItem>
<TabItem label="Kotlin">

```kotlin
import io.cratis.arc.concepts.ConceptAs
import java.math.BigDecimal
import java.util.UUID

data class AccountName(private val rawValue: String) : ConceptAs<String> {
    override fun value(): String = rawValue
}

data class AccountBalance(private val rawValue: BigDecimal) : ConceptAs<BigDecimal> {
    override fun value(): BigDecimal = rawValue
}

interface AccountService {
    suspend fun open(name: AccountName, initialBalance: AccountBalance): UUID
}

@Command
data class OpenDebitAccount(val name: AccountName, val initialBalance: AccountBalance) {
    suspend fun handle(@FromServices accounts: AccountService): UUID =
        accounts.open(name, initialBalance)
}
```

[View Kotlin snippet source on GitHub](https://github.com/Cratis/Arc.Kotlin/blob/main/Documentation/client-snippets/frontend/react/proxy-generation/open-debit-account.md)

</TabItem>
<TabItem label="TypeScript (preview)">

```typescript
import { ConceptAs, field, Guid } from '@cratis/fundamentals';
import { command, inject } from '@cratis/arc.core';

export class AccountName extends ConceptAs<string> {
    static readonly valueType = String;
}

export class AccountBalance extends ConceptAs<number> {
    static readonly valueType = Number;
}

export abstract class AccountService {
    abstract open(name: AccountName, initialBalance: AccountBalance): Promise<Guid>;
}

@command()
export class OpenDebitAccount {
    @field(AccountName) name!: AccountName;
    @field(AccountBalance) initialBalance!: AccountBalance;

    @inject(AccountService)
    handle(accounts: AccountService): Promise<Guid> {
        return accounts.open(this.name, this.initialBalance);
    }
}
```

[View TypeScript (preview) snippet source on GitHub](https://github.com/Cratis/Arc.TypeScript/blob/main/Documentation/client-snippets/frontend/react/proxy-generation/open-debit-account.md)

</TabItem>
</Tabs>

The named concepts keep account names and balances distinct in backend signatures. Their generated client properties use the underlying primitive — here `string` and `number` — so the form does not need to construct concept objects. Keep each concept in its own file when organizing your application.

After generation, use the proxy from a React component. This consumer fragment assumes its generated import path is `./generated/OpenDebitAccount`:

```tsx
import { OpenDebitAccount } from './generated/OpenDebitAccount';

export function OpenAccountButton() {
    const [command] = OpenDebitAccount.use({
        name: 'Primary account',
        initialBalance: 500
    });

    async function open() {
        const result = await command.execute();
        if (result.isSuccess) {
            console.log(String(result.response));
        }
    }

    return <button onClick={() => void open()}>Open account</button>;
}
```

An ordinary `Guid`/`UUID` response remains a supported response and maps to Fundamentals `Guid`. The [Chronicle integration](/arc/backend/csharp/chronicle/commands/) adds optional event-sourcing behavior; it does not redefine generic Arc responses.

The command hook returns `[command, setValues, clearValues]`. Set required content explicitly. The setter edits properties; it does not reset the change-tracking baseline. See [data binding](/arc/frontend/react/commands/data-binding/).

## Queries follow the backend result shape

Both [controller-based](/arc/backend/csharp/queries/controller-based/) and [model-bound](/arc/backend/csharp/queries/model-bound/) C# endpoints generate proxies; the JVM generates from model-bound `@ReadModel` query methods only — it has no controller-based discovery. TypeScript's source generator reads `@readModel()` classes with static `@query()` methods; see its [proxy generation overview](/arc/backend/typescript/proxy-generation/) for supported output. Parameterized queries emit a `NameParameters` interface. Query hooks return tuples, not model objects; read the first tuple element's `.data`.

- Ordinary `.use()` returns `[result, perform, setSorting]`.
- Observable enumerable `.use()` returns `[result, setSorting]`.
- Observable single-model `.use()` returns `[result]`.
- Only enumerable proxies expose `.useWithPaging()`, `.useSuspenseWithPaging()`, and, for observables, `.useChangeStream()`.

See [query usage](/arc/frontend/react/queries/usage/) for argument positions and the complete tuple matrix. Results expose `isPerforming`, `isReady`, `isAuthorized`, `isValid`, `hasExceptions`, and `exceptionMessages`; they do not expose an `error` or `isLoading` property.

## Live and Suspense consumers

Observable proxies subscribe to an emitting backend source. MongoDB observation is one option; Chronicle is another optional integration. A database write does not become a live notification without that source wiring. Both [SSE and WebSocket](/arc/frontend/react/queries/observable-query-multiplexing/) support direct and shared-hub modes.

All generated queries expose `.useSuspense()`. Suspense handles initial waiting and selected failures, not every unsuccessful result: keep validation/readiness handling in the component. Read [Suspense limitations](/arc/frontend/react/queries/suspense-queries/) before adding an error boundary or retry control.
