Schemas Overview
ReFormer uses a three-schema architecture to separate concerns and enable maximum code reuse.
The Three Schemasβ
| Schema | Purpose | Property |
|---|---|---|
| Form Schema | Data structure and field configuration | form |
| Validation Schema | Validation rules | validation |
| Behavior Schema | Reactive logic and side effects | behavior |
import { GroupNode } from '@reformer/core';
import { required, email } from '@reformer/core/validators';
import { computeFrom } from '@reformer/core/behaviors';
const form = new GroupNode({
// 1. Form Schema - structure
form: {
firstName: { value: '' },
lastName: { value: '' },
fullName: { value: '' },
email: { value: '' },
},
// 2. Validation Schema - rules
validation: (path) => {
required(path.firstName);
required(path.lastName);
required(path.email);
email(path.email);
},
// 3. Behavior Schema - logic
behavior: (path) => {
computeFrom([path.firstName, path.lastName], path.fullName, ({ firstName, lastName }) =>
`${firstName} ${lastName}`.trim()
);
},
});
Why Three Schemas?β
Separation of Concernsβ
Each schema has a single responsibility:
- Form Schema: "What data do we collect?"
- Validation Schema: "Is the data correct?"
- Behavior Schema: "How should data react to changes?"
Reusability & Decompositionβ
Each schema can be decomposed into reusable parts and combined using the apply function:
import { apply, required } from '@reformer/core/validators';
import { apply as applyBehavior, watchField } from '@reformer/core/behaviors';
// 1. Reusable form schema (always use factory functions!)
const addressSchema = (): FormSchema<Address> => ({
street: { value: '' },
city: { value: '' },
zipCode: { value: '' },
});
// 2. Reusable validation schema
const addressValidation: ValidationSchemaFn<Address> = (path) => {
required(path.street);
required(path.city);
required(path.zipCode);
};
// 3. Reusable behavior schema
const addressBehavior: BehaviorSchemaFn<Address> = (path) => {
watchField(path.zipCode, (value, ctx) => {
// Format ZIP code
});
};
// Compose into forms using apply()
const orderForm = new GroupNode<OrderForm>({
form: {
billingAddress: addressSchema(),
shippingAddress: addressSchema(),
},
validation: (path) => {
// Apply same validation to multiple fields
apply([path.billingAddress, path.shippingAddress], addressValidation);
},
behavior: (path) => {
// Apply same behavior to multiple fields
applyBehavior([path.billingAddress, path.shippingAddress], addressBehavior);
},
});
The apply function supports flexible composition:
// Single field + single schema
apply(path.address, addressValidation);
// Multiple fields + single schema
apply([path.billingAddress, path.shippingAddress], addressValidation);
// Single field + multiple schemas
apply(path.email, [requiredValidation, emailValidation]);
// Multiple fields + multiple schemas
apply([path.email, path.phone], [requiredValidation, formatValidation]);
Factory Functions
Always use functions that return schemas (addressSchema()) instead of direct objects. This ensures each form gets its own instance and avoids shared state bugs.
Benefits of decomposition:
- DRY β Write once, use everywhere
- Consistency β Same rules across all forms
- Maintainability β Update in one place
- Testing β Test each part in isolation
See Composition for complete patterns and best practices.
Testabilityβ
Test each schema in isolation:
// Test validation separately
describe('validatePerson', () => {
it('requires firstName', () => {
const form = new GroupNode({
form: personSchema(),
validation: validatePerson,
});
expect(form.controls.firstName.errors).toEqual({ required: true });
});
});
Type Safetyβ
All three schemas use FieldPath<T> for compile-time type checking:
validation: (path) => {
required(path.firstName); // β
TypeScript knows this exists
required(path.middleName); // β Error: 'middleName' doesn't exist
};
Schema Structureβ
GroupNode Config
βββ form: FormSchema<T> β Data structure
βββ validation: ValidationSchemaFn<T> β Validation rules
βββ behavior: BehaviorSchemaFn<T> β Reactive logic
Next Stepsβ
- Form Schema β Structure and field configuration
- Validation Schema β Validation rules
- Behavior Schema β Reactive logic
- Composition β Reuse and decomposition patterns