Skip to main content

Validation

Forma provides two levels of validation: schema-based validation from JSON Schema, and custom validation rules using FEEL expressions.

Schema Validation

Basic validation is handled automatically by the JSON Schema:

{
"schema": {
"properties": {
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0, "maximum": 120 },
"password": { "type": "string", "minLength": 8 }
},
"required": ["email", "age"]
}
}

Schema validation covers:

  • Required fields
  • Type checking (string, number, integer, boolean)
  • String length (minLength, maxLength)
  • Number ranges (minimum, maximum)
  • Numeric precision (multipleOf)
  • Format validation (email, date, uri)
  • Enum values
  • Array item count (minItems, maxItems)
  • Nested constraints within array items

Custom Validation Rules

For validation logic that can't be expressed in JSON Schema, use the validations property:

{
"fields": {
"endDate": {
"label": "End Date",
"validations": [
{
"rule": "date(value) > date(startDate)",
"message": "End date must be after start date"
}
]
}
}
}

Array Validation

Arrays are validated for both item count and nested item constraints.

Item Count (minItems/maxItems)

Define item count constraints in the schema:

{
"schema": {
"properties": {
"participants": {
"type": "array",
"items": {
"type": "object",
"properties": { "name": { "type": "string" } }
},
"minItems": 2,
"maxItems": 10
}
}
}
}

Field definitions can override schema constraints:

{
"fields": {
"participants": {
"label": "Participants",
"type": "array",
"minItems": 1,
"maxItems": 5
}
}
}

When both are specified, fields.minItems/maxItems takes precedence over schema.minItems/maxItems.

Nested Item Validation

Constraints on array item properties are automatically validated:

{
"schema": {
"properties": {
"scores": {
"type": "array",
"items": {
"type": "object",
"properties": {
"studentName": { "type": "string", "minLength": 1 },
"score": { "type": "number", "minimum": 0, "maximum": 100 }
},
"required": ["studentName", "score"]
},
"minItems": 1
}
}
}
}

This validates:

  • At least 1 item in the array
  • Each item has studentName (required, non-empty string)
  • Each item has score (required, between 0-100)

Custom Validation in Array Items

Use itemFields for custom FEEL validation rules on array items:

{
"fields": {
"orders": {
"label": "Orders",
"type": "array",
"itemFields": {
"quantity": {
"label": "Quantity",
"validations": [
{
"rule": "value <= 100",
"message": "Cannot order more than 100 items"
}
]
}
}
}
}
}

Conditional Required in Array Items

Use requiredWhen for conditional requirements within array items:

{
"fields": {
"contacts": {
"type": "array",
"itemFields": {
"phone": {
"label": "Phone",
"requiredWhen": "item.contactMethod = \"phone\""
},
"email": {
"label": "Email",
"requiredWhen": "item.contactMethod = \"email\""
}
}
}
}
}

The item variable refers to the current array item, and itemIndex provides the 0-based index.

Validation Rule Structure

{
"validations": [
{
"rule": "...",
"message": "...",
"severity": "error"
}
]
}
PropertyTypeRequiredDescription
rulestringYesFEEL expression that must evaluate to true
messagestringYesError message shown when validation fails
severitystringNo"error" (default) or "warning"

The value Variable

In validation rules, value refers to the current field's value:

{
"username": {
"validations": [
{
"rule": "string length(value) >= 3",
"message": "Username must be at least 3 characters"
}
]
}
}

Cross-Field Validation

Reference other fields to create cross-field validation:

{
"confirmPassword": {
"validations": [
{
"rule": "value = password",
"message": "Passwords must match"
}
]
}
}

Severity Levels

Error (default)

Errors prevent form submission:

{
"rule": "value >= 18",
"message": "You must be 18 or older",
"severity": "error"
}

Warning

Warnings are informational and don't block submission:

{
"rule": "value <= 65",
"message": "Please verify age for senior discount eligibility",
"severity": "warning"
}

Common Validation Patterns

String Validation

Minimum length:

{
"rule": "string length(value) >= 5",
"message": "Must be at least 5 characters"
}

Maximum length:

{
"rule": "string length(value) <= 100",
"message": "Cannot exceed 100 characters"
}

Pattern matching:

{
"rule": "matches(value, \"^[A-Z]{2}[0-9]{6}$\")",
"message": "Must be 2 letters followed by 6 digits (e.g., AB123456)"
}

Number Validation

Range:

{
"rule": "value >= 0 and value <= 100",
"message": "Must be between 0 and 100"
}

Must be positive:

{
"rule": "value > 0",
"message": "Must be a positive number"
}

Date Validation

Future date:

{
"rule": "date(value) > today()",
"message": "Date must be in the future"
}

Past date:

{
"rule": "date(value) < today()",
"message": "Date must be in the past"
}

Date range:

{
"rule": "date(value) >= date(startDate) and date(value) <= date(endDate)",
"message": "Date must be within the specified range"
}

Cross-Field Validation

Confirm match:

{
"rule": "value = originalField",
"message": "Values must match"
}

Date ordering:

{
"rule": "date(value) > date(startDate)",
"message": "End date must be after start date"
}

Conditional validation:

{
"rule": "if hasDiscount then value > 0 else true",
"message": "Discount amount is required when discount is enabled"
}

Using Computed Values

Reference computed values for complex validation:

{
"rule": "computed.total <= budget",
"message": "Order total exceeds available budget"
}

Multiple Validations

A field can have multiple validation rules:

{
"password": {
"validations": [
{
"rule": "string length(value) >= 8",
"message": "Password must be at least 8 characters"
},
{
"rule": "matches(value, \"[A-Z]\")",
"message": "Password must contain an uppercase letter"
},
{
"rule": "matches(value, \"[0-9]\")",
"message": "Password must contain a number"
}
]
}
}

Validation Order

  1. Schema validation runs first (type, format, required)
  2. Custom validation rules run when the field has a non-empty value, independently of type validation. All errors are collected together
  3. Hidden fields (visibleWhen = false) are not validated

Best Practices

  1. Use schema validation first - Put type constraints, ranges, and formats in the schema

  2. Keep messages user-friendly - Write clear, actionable error messages

  3. Validate early - Add validation to catch errors as users type

  4. Use warnings for soft validation - When you want to alert but not block

  5. Test edge cases - Consider null values, empty strings, and boundary conditions