Skip to content

Validation (Server Perspective) ​

Summary ​

Validation runs at three levels. You control all of them. This page explains what to put where.

The three layers ​

Layer 1: Local validation rules (client-side, instant)
  Your server defines rules in the field's `validation` object.
  The client enforces them without a network call.

Layer 2: Blur validation (server-side, per-field)
  For lookup fields with `validate` endpoint.
  The client calls your server when the user leaves the field.

Layer 3: Save validation (server-side, full changeset)
  Your save handler validates the complete changeset.
  The final gate before data is persisted.

Layer 1: Local validation rules ​

Add a validation object to the field definition. The client enforces these rules instantly when the user confirms a field edit:

json
{
  "id": "name",
  "label": "Name",
  "type": "Text",
  "validation": {
    "required": true,
    "max_length": 100
  }
}

If validation fails, the user is locked on the field until they fix the value or press Esc to revert.

Available rules ​

RuleTypeWhen to use
requiredbooleanField must not be empty
max_lengthintegerMaximum character count
min_lengthintegerMinimum character count
patternregexValue must match pattern (e.g., email format)
input_maskstringFilter keystrokes: "uppercase", "lowercase", "digits_only"
min / maxfloatNumeric bounds for Decimal/Integer fields
decimalsintegerMaximum decimal places

What to put here ​

  • Format constraints (length, pattern, character filtering)
  • Required fields
  • Numeric bounds
  • Anything that can be checked without server access

What NOT to put here ​

  • Uniqueness checks (requires database)
  • Relational validation (use blur validation)
  • Cross-field rules (use save validation)

Layer 2: Blur validation ​

For fields bound to a related table (lookup fields), add a validate endpoint to the lookup definition:

json
{
  "lookup": {
    "endpoint": "/lookup/post_code",
    "validate": "/validate/post_code"
  }
}

See Lookups for the full specification.

What to put here ​

  • "Does this value exist in the related table?"
  • Autofill values (fill related fields automatically)

What NOT to put here ​

  • Business rules that depend on other fields
  • Rules that depend on the full save context

Layer 3: Save validation ​

Your save handler receives the full changeset. Validate everything:

json
POST /screen/customer_card/10000/save
{
  "changes": {
    "name": "",
    "post_code": "100"
  }
}

If validation fails, return HTTP 200 with the error in status:

json
{
  "layout": "Card",
  "status": "Cannot save: Name is required.",
  "sections": []
}

What to put here ​

  • Cross-field validation (e.g., "end date must be after start date")
  • Business rules (e.g., "credit limit exceeded")
  • Uniqueness constraints
  • Permission checks
  • Anything that requires the full changeset or database state

Don't duplicate validation ​

Your validate endpoint and save handler should share the same underlying checks. Don't implement postal code validation in two places — extract it into a shared function.

Error messages ​

  • Be specific: "'999' is not a valid postal code." not "Validation failed."
  • Include the rejected value when appropriate
  • Use the field label: "Name is required." not "Field is required."