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.
Purpose
Section titled “Purpose”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
How It Works
Section titled “How It Works”When you validate a command, the request is sent to the backend validation endpoint where:
- All command filters run (authorization, validation)
- The command handler is not executed
- A
CommandResultis returned with validation and authorization status - No side effects occur on the system
For details on the backend validation pipeline, see Backend Command Validation.
Command.validate() Method
Section titled “Command.validate() Method”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>>;}Basic Usage
Section titled “Basic Usage”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); } }}CommandResult Structure
Section titled “CommandResult Structure”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.
Validation Filters
Section titled “Validation Filters”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.
Best Practices
Section titled “Best Practices”When to Use Validate
Section titled “When to Use Validate”✅ 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
Performance Considerations
Section titled “Performance Considerations”- 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
Example: Debounced Validation
Section titled “Example: Debounced Validation”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);}
// Usageconst command = new CreateOrder();command.orderNumber = 'ORD-12345';
debounceValidation(command, (result) => { if (!result.isSuccess) { console.log('Validation errors:', result.validationResults); }});Security Considerations
Section titled “Security Considerations”- 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)
Framework-Specific Usage
Section titled “Framework-Specific Usage”For React-specific patterns and hooks, see React Command Validation.