Skip to main content
View as Markdown

Validation

Sovrium validates data at two layers: config-time (the schema is decoded with Effect Schema when the app boots, catching invalid definitions) and runtime (records are checked against field rules, constraints, and permissions on every write). This page summarizes the validation surface for tables.

Field-Level Validation

Many field types carry built-in validation derived from their properties:

Rule Applies to Effect
required All fields Value must be present on every record.
unique All fields No two records may share the value.
min / max integer, decimal, currency, percentage Value must fall within the inclusive range.
precision decimal (1–10), currency/percentage (0–10) Restricts decimal places.
currency (ISO 4217) currency Must be a valid three-letter code.
maxLength rich-text Caps character length.
maxSelections multi-select Caps choices; cannot exceed the number of options.
options single-select, multi-select, status Values must come from the declared option list.
format validation email, url Must match RFC 5322 / a valid absolute URL.
targetLanguage ai-translate Must be an ISO 639-1 code (optionally region-suffixed).
categories / tags ai-categorize / ai-tag Minimum 2 entries, no duplicates.

Table-Level Validation (Config Time)

When the schema is decoded, Sovrium enforces structural rules and rejects invalid configurations with descriptive errors:

  • Field names and IDs must be unique within a table.
  • Field & table names must match ^[a-z][a-z0-9_]* (after sanitization) and stay within 63 characters.
  • Index names and constraint names must be unique within the table and match the identifier pattern.
  • Primary key fields must reference real fields.
  • Indexes, permissions, views, webhooks are checked so their field references name actual table fields.
  • count / rollup / lookup fields must reference an existing relationship field in the same table.
  • Formula fields are validated for correct field references.
  • Webhook names must be unique, and payload field selectors must name real fields (the implicit id is always valid).

CHECK Constraints

Use CHECK constraints for cross-field and conditional rules that field-level validation cannot express:

constraints:
  - { name: chk_end_after_start, check: 'end_date > start_date' }
  - { name: chk_active_members_have_email, check: '(is_active = false) OR (email IS NOT NULL)' }

Uniqueness & Integrity

  • Single-field uniqueness: field.unique: true or a top-level unique: [{ fields: [x] }].
  • Composite uniqueness: top-level unique: [{ fields: [a, b] }] (becomes a unique index) or a unique index.
  • Partial uniqueness: a unique index with a where clause (e.g. unique slug only among non-deleted rows).
  • Relational integrity: relationship fields create foreign keys with onDelete/onUpdate actions; composite references use foreignKeys.