Skip to content

Command Validation

Command validation enables pre-flight validation of commands without executing them. This provides early feedback to users before performing potentially expensive or state-changing operations.

The validation mechanism allows you to check authorization and validation rules without executing the command handler. This is essential for:

  • Early User Feedback: Show validation errors before the user submits a form
  • UX Improvements: Enable/disable submit buttons based on validation state
  • Authorization Checks: Verify user permissions without side effects
  • Progressive Validation: Validate fields as users interact with forms

When you validate a command, the request is sent to the backend validation endpoint where:

  1. All command filters run (authorization, validation)
  2. The command handler is not executed
  3. A CommandResult is returned with validation and authorization status
  4. No side effects occur on the system

For details on the backend validation pipeline, see Backend Command Validation.

All generated TypeScript command proxies include a validate() method alongside the execute() method:

interface ICommand<TCommandContent, TCommandResponse> {
/**
* Validate the command without executing it.
* Returns validation and authorization status.
*/
validate(): Promise<CommandResult<TCommandResponse>>;
/**
* Execute the command.
*/
execute(): Promise<CommandResult<TCommandResponse>>;
}
import { CreateOrder } from './generated/commands';
async function validateOrder() {
const command = new CreateOrder();
command.orderNumber = 'ORD-12345';
command.customerId = '550e8400-e29b-41d4-a716-446655440000';
// Validate without executing
const result = await command.validate();
if (result.isSuccess) {
console.log('Command is valid and authorized');
} else {
if (!result.isAuthorized) {
console.log('User not authorized');
}
if (!result.isValid) {
console.log('Validation errors:', result.validationResults);
}
}
}

Both execute() and validate() return the same CommandResult structure:

interface CommandResult<TResponse> {
correlationId: string;
isSuccess: boolean; // Overall success (authorized + valid + no exceptions)
isAuthorized: boolean; // Authorization status
isValid: boolean; // Validation status
hasExceptions: boolean; // Whether exceptions occurred
validationResults: ValidationResult[];
exceptionMessages: string[];
exceptionStackTrace: string;
response?: TResponse; // Only populated on execute()
}
interface ValidationResult {
message: string;
members: string[];
severity: 'Error' | 'Warning' | 'Info';
}

Important: The response property will be null or undefined when using validate() since the handler is not executed.

The validate() method runs all registered command filters on the backend:

  • AuthorizationFilter: Checks user permissions
  • DataAnnotationValidationFilter: Validates data annotations
  • FluentValidationFilter: Runs FluentValidation validators

For more information, see Backend Command Filters.

Good Use Cases:

  • Form validation as users type or blur fields
  • Enabling/disabling submit buttons based on validation state
  • Showing validation messages before submission
  • Checking authorization before showing UI elements

Avoid:

  • Calling validate() immediately before execute() (execute already validates)
  • Over-validating (don’t validate on every keystroke for performance)
  • Using validate() as a substitute for client-side validation
  • Validation makes a server round-trip, so use judiciously
  • Consider debouncing validation calls for real-time feedback
  • Client-side validation is still important for immediate feedback
  • Server validation ensures security and data integrity
let validationTimeout: NodeJS.Timeout;
function debounceValidation(command: ICommand<any, any>, onResult: (result: CommandResult<any>) => void) {
clearTimeout(validationTimeout);
validationTimeout = setTimeout(async () => {
const result = await command.validate();
onResult(result);
}, 500);
}
// Usage
const command = new CreateOrder();
command.orderNumber = 'ORD-12345';
debounceValidation(command, (result) => {
if (!result.isSuccess) {
console.log('Validation errors:', result.validationResults);
}
});
  • Validation endpoints run the same authorization filters as execute endpoints
  • Unauthorized users receive 401/403 responses from validation endpoints
  • Validation does not expose sensitive data since handlers aren’t executed
  • Validation results may reveal authorization policies (by design)

For React-specific patterns and hooks, see React Command Validation.