Skip to content

Command proxy generation

The proxy generator creates TypeScript command classes that provide type-safe command execution with React hook integration.

Commands can be implemented using two approaches, both of which are supported by the proxy generator:

  • Controller-based: Commands in ASP.NET Core controllers using [HttpPost] attributes
  • Model-bound: Simplified approach where a type represents the command directly

For detailed information on implementing commands, see the Commands documentation.

The generator discovers controller-based commands by looking for:

  • Methods marked with [HttpPost]
  • Parameters marked with [FromBody], [FromRoute], or [FromQuery]

See Controller-based Commands for implementation details.

The generator discovers model-bound commands by finding types that:

  • Are decorated with the [Command] attribute
  • Have a Handle() method (the command handler)

The type name becomes the command name. Public instance properties declared directly on the command type become command properties in the generated TypeScript; this pass does not collect inherited properties.

See Model-bound Commands for implementation details.

Generated command classes:

  • Extend the Command base class from @cratis/arc/commands
  • Include all properties from route parameters, query parameters, and body content
  • Provide a static use() method for React hook integration
  • Include the proper route based on the configuration

For each command, the generator creates:

  1. Interface: An ICommandName interface with all command properties
  2. Class: A CommandName class extending Command<ICommandName>, or Command<ICommandName, TResponse> for a response-bearing command
  3. Route: The HTTP route derived from the controller route or model-bound configuration

Command operations declare inline work rather than response DTOs. The generator excludes ICommandOperation implementations and CommandOperations batches from the caller’s response, including supported tuple and result alternatives. A tuple containing a response and operations produces a response-bearing proxy for the response alone; an operation-only command has no response payload.

No operation execution or compensation code is generated for the frontend. Backend recovery observations are also excluded from HTTP JSON. Clients continue to use the existing CommandResult contract.

Operation method invokers are generated by Arc’s C# source generator. TypeScript proxies are still produced by the separate post-build proxy generator; these are different generation steps.

To exclude specific controller-based commands from proxy generation, mark them with the [AspNetResult] attribute. This is useful when you want to handle the response manually or when the command returns a non-standard result.

For conventional model-bound commands, the generated route is affected by the CratisProxiesSkipCommandNameInRoute configuration option. Controller routes come from their ASP.NET declarations, not this switch:

  • When false (default): The command type name is included in the route
  • When true: The command type name is excluded from the route

Automatic Conflict Detection: When CratisProxiesSkipCommandNameInRoute is true, the proxy generator automatically detects if multiple commands exist in the same namespace (after skipping segments). If a conflict is detected, the command name is automatically included in the route to prevent route collisions. Align the generator’s settings with the runtime endpoint settings; changing the build configuration does not configure the server.

For example:

  • Single command in namespace: Route is clean without type name (e.g., /api/orders)
  • Multiple commands in same namespace: Type names are added automatically (e.g., /api/orders/create-order, /api/orders/update-order)

The examples assume the application namespace prefix has been skipped. See routing configuration for segment counts, defaults, and runtime alignment.

The generated command proxies integrate with React through the use() static method, which returns:

  1. The command instance with all properties.
  2. A SetCommandValues<ICommandName> setter for editing values.
  3. A ClearCommandValues function.

The signature is use(initialValues?: ICommandName): [CommandName, SetCommandValues<ICommandName>, ClearCommandValues]. This is an API shape, not a manually authored replacement for the generated class. The setter edits values; it does not establish a new change-tracking baseline.

Await execute() to receive a CommandResult with success/failure information, validationResults, and the typed response when applicable.

For frontend usage patterns, see React commands. Commands can return ordinary application responses, including Guid; proxy generation does not require event sourcing.