This guide covers the complete syntax for formulas and validation rules in ObjectQL. Understanding these patterns will help you create powerful calculated fields and enforce business rules.
Formulas are used to create calculated fields that derive their values from other fields or expressions. They are read-only and automatically computed.
# Simple field reference - use field name directlycalc_total: type: formula expression: "price * quantity" data_type: number# With curly braces (alternative syntax)calc_total_alt: type: formula expression: "{price} * {quantity}" data_type: number
# Date differencedays_open: type: formula expression: "$today - created_date" data_type: number# Date comparisonis_overdue: type: formula expression: "due_date < $today && status != 'completed'" data_type: boolean
# Access related object fields using dot notationaccount_owner_name: type: formula expression: "customer.account.owner.name" data_type: text# Nested lookupsregion_manager: type: formula expression: "account.region.manager.email" data_type: text
# Age calculationdays_since_created: type: formula expression: "$today - created_date" data_type: number# Is expiredis_expired: type: formula expression: "expiration_date < $today" data_type: boolean# Durationproject_duration_days: type: formula expression: "end_date - start_date" data_type: number
# In object.ymlfields: email: type: email required: true validation: format: email message: "Please enter a valid email address" age: type: number validation: min: 0 max: 150 message: "Age must be between 0 and 150" username: type: text required: true validation: min_length: 3 max_length: 20 pattern: "^[a-zA-Z0-9_]+$" message: "Username must be 3-20 alphanumeric characters"
# In project.validation.ymlrules: - name: valid_date_range type: cross_field rule: field: end_date operator: ">=" compare_to: start_date message: "End date must be on or after start date" error_code: "INVALID_DATE_RANGE"
rules: - name: safe_division type: custom validator: | function validate(record) { if (record.denominator === 0) { return false; } return (record.numerator / record.denominator) < 100; } message: "Result must be less than 100 (division by zero not allowed)"
# AND - All conditions must be truecondition: all_of: - field: status operator: "=" value: active - field: budget operator: ">" value: 1000# OR - At least one condition must be truecondition: any_of: - field: priority operator: "=" value: high - field: amount operator: ">" value: 10000# NOT - Condition must be falsecondition: none_of: - field: status operator: "=" value: deleted
Keep formulas simple - Complex logic should be in hooks or actions
Use appropriate data types - Specify correct data_type for the result
Handle null values - Use conditional checks for optional fields
Document complex formulas - Add comments explaining business logic
Test edge cases - Verify division by zero, null values, etc.
# Good - Simple and cleardiscount_amount: type: formula expression: "list_price * discount_rate" data_type: currency# Good - Handles nullfull_name: type: formula expression: "(first_name || '') + ' ' + (last_name || '')" data_type: text# Avoid - Too complex for a formulacomplex_score: type: formula expression: | var base = weight * coefficient; for (var i = 0; i < iterations; i++) { base = base * (1 + rate); } return base; data_type: number # Better: Move to a hook or action
Use declarative rules when possible - Prefer cross_field over custom
Provide clear error messages - Help users understand what's wrong
Set appropriate severity - Use error, warning, or info
Order rules by likelihood of failure - Fast-fail optimization
Use AI context - Help AI tools understand business intent
# Good - Clear message and intentrules: - name: valid_date_range type: cross_field ai_context: intent: "Ensure timeline is logical" business_rule: "Projects cannot end before they start" rule: field: end_date operator: ">=" compare_to: start_date message: "End date must be on or after start date" severity: error# Good - Conditional validation - name: approval_required_for_high_value type: conditional condition: field: amount operator: ">" value: 50000 rule: field: approved_by operator: "not_empty" message: "Approval required for transactions over $50,000" severity: error