---
title: Validation
description: Validation protocol schemas
---

{/* ⚠️  AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs go in content/docs/guides/. */}

# ObjectStack Validation Protocol

This module defines the validation schema protocol for ObjectStack, providing a comprehensive

type-safe validation system similar to Salesforce's validation rules but with enhanced capabilities.

## Overview

Validation rules are applied at the data layer to ensure data integrity and enforce business logic.

A validation rule is a **deterministic, synchronous, side-effect-free predicate over a single

record** — it must be decidable from the incoming write (and, on update, the prior record) with

no I/O. Everything advertised here runs on the write path (see

`objectql/src/validation/rule-validator.ts`); nothing is a silent no-op.

The system supports these validation types:

1. **Script Validation**: Formula-based validation using a CEL predicate

2. **State Machine Validation**: Control allowed state transitions

3. **Format Validation**: Validate a field's value (email, URL, phone, JSON, regex)

4. **Cross-Field Validation**: Validate relationships between multiple fields

5. **JSON Schema Validation**: Validate a JSON field against a JSON Schema

6. **Conditional Validation**: Apply a nested rule based on a CEL condition

## Deliberately NOT validation rules

These were once declared here but never enforced. Because the contract above rules them out

(they need I/O or are client-side concerns), they were removed rather than left as silent

no-ops. Use the layer that already does each one correctly:

- **Uniqueness** → a unique **index** (`ObjectSchema.indexes`, `\{ fields, unique: true \}`,

with `partial` for a scoped/conditional constraint), or field-level `unique: true`. A

SELECT-then-INSERT "rule" is inherently racy (TOCTOU); a DB unique constraint is not.

- **Async / remote validation** → a client-form concern (`debounce`/`validatorUrl` only mean

anything against keystrokes) and an SSRF/latency hazard on the server write path. Keep it in

the form layer, or enforce the underlying invariant with a `unique` index / lifecycle hook.

- **Custom handler** → a `beforeInsert` / `beforeUpdate` lifecycle hook, the typed, supported

extension point for arbitrary validation code.

## Salesforce Comparison

ObjectStack validation rules are inspired by Salesforce validation rules but enhanced:

- Salesforce: Formula-based validation with `Error Condition Formula`

- ObjectStack: Multiple validation types with composable rules

Example Salesforce validation rule:

```

Rule Name: Discount_Cannot_Exceed_40_Percent

Error Condition Formula: Discount_Percent__c > 0.40

Error Message: Discount cannot exceed 40%.

```

Equivalent ObjectStack rule:

```typescript

\{

type: 'script',

name: 'discount_cannot_exceed_40_percent',

condition: 'discount_percent > 0.40',

message: 'Discount cannot exceed 40%',

severity: 'error'

\}

```

<Callout type="info">
**Source:** `packages/spec/src/data/validation.zod.ts`
</Callout>

## TypeScript Usage

```typescript
import { FormatValidation, JSONValidation, StateMachineValidation } from '@objectstack/spec/data';
import type { FormatValidation, JSONValidation, StateMachineValidation } from '@objectstack/spec/data';

// Validate data
const result = FormatValidation.parse(data);
```

---

## FormatValidation

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Unique rule name (snake_case) |
| **label** | `string` | optional | Human-readable label for the rule listing |
| **description** | `string` | optional | Administrative notes explaining the business reason |
| **active** | `boolean` | ✅ |  |
| **events** | `Enum<'insert' \| 'update' \| 'delete'>[]` | ✅ | Validation contexts |
| **priority** | `integer` | ✅ | Execution priority (lower runs first, default: 100) |
| **tags** | `string[]` | optional | Categorization tags (e.g., "compliance", "billing") |
| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | ✅ |  |
| **message** | `string` | ✅ | Error message to display to the user |
| **type** | `string` | ✅ |  |
| **field** | `string` | ✅ |  |
| **regex** | `string` | optional |  |
| **format** | `Enum<'email' \| 'url' \| 'phone' \| 'json'>` | optional |  |


---

## JSONValidation

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Unique rule name (snake_case) |
| **label** | `string` | optional | Human-readable label for the rule listing |
| **description** | `string` | optional | Administrative notes explaining the business reason |
| **active** | `boolean` | ✅ |  |
| **events** | `Enum<'insert' \| 'update' \| 'delete'>[]` | ✅ | Validation contexts |
| **priority** | `integer` | ✅ | Execution priority (lower runs first, default: 100) |
| **tags** | `string[]` | optional | Categorization tags (e.g., "compliance", "billing") |
| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | ✅ |  |
| **message** | `string` | ✅ | Error message to display to the user |
| **type** | `string` | ✅ |  |
| **field** | `string` | ✅ | JSON field to validate |
| **schema** | `Record<string, any>` | ✅ | JSON Schema object definition |


---

## StateMachineValidation

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **name** | `string` | ✅ | Unique rule name (snake_case) |
| **label** | `string` | optional | Human-readable label for the rule listing |
| **description** | `string` | optional | Administrative notes explaining the business reason |
| **active** | `boolean` | ✅ |  |
| **events** | `Enum<'insert' \| 'update' \| 'delete'>[]` | ✅ | Validation contexts |
| **priority** | `integer` | ✅ | Execution priority (lower runs first, default: 100) |
| **tags** | `string[]` | optional | Categorization tags (e.g., "compliance", "billing") |
| **severity** | `Enum<'error' \| 'warning' \| 'info'>` | ✅ |  |
| **message** | `string` | ✅ | Error message to display to the user |
| **type** | `string` | ✅ |  |
| **field** | `string` | ✅ | State field (e.g. status) |
| **transitions** | `Record<string, string[]>` | ✅ | Map of `{ OldState: [AllowedNewStates] }` |


---

