Validation

Constraints Plugin

Enforce business rules and relationships between items using constraints.

The Constraints Plugin allows you to enforce complex business rules and relationships between items in a desk. It supports a wide variety of constraint types, making it ideal for scenarios where data consistency and inter-item logic are required.

Installation

import { useCheckIn } from 'vue-airport';
import { createConstraintsPlugin, ConstraintType } from '@vue-airport/plugins-validation';

interface Member {
  email: string;
  role: string;
  age?: number;
}

const constraints = [
  { type: ConstraintType.Unique, key: 'email', message: 'Email already used' },
  { type: ConstraintType.MaxCount, count: 5, message: 'Maximum 5 members' },
  { type: ConstraintType.Required, key: 'role', message: 'Role is required' },
  { type: ConstraintType.Enum, key: 'role', values: ['admin', 'user'], message: 'Role must be admin or user' },
  {
    type: ConstraintType.Custom,
    fn: (child, children) => child.role === 'admin' && children.filter(u => u.role === 'admin').length >= 2
      ? 'Maximum 2 admins'
      : null,
    message: 'Maximum 2 admins'
  },
];

const { desk } = useCheckIn<Member>({
  plugins: [createConstraintsPlugin(constraints)]
});

Supported Constraint Types

  • Custom: Custom validation logic, sync or async.
  • Range: Ensures a numeric field is within a given range.
  • Immutable: Prevents modification of a field after creation.
  • Forbidden: Forbids certain values or combinations.
  • Compare: Compares two fields with a given operator (<, >, <=, >=, ===, !==).
  • ConditionalRequired: Requires a field if a condition on another field is met.
  • Format: Validates the format of a field (email, url, phone, etc).
  • MinValue: Requires a numeric field to be at least a minimum value.
  • MaxValue: Requires a numeric field to be at most a maximum value.
  • UniqueInScope: Ensures uniqueness within a sub-scope/group.
  • Exists: Checks that a reference exists in another collection.
  • Unique: Ensures the value of a given key is unique among all children.
  • MaxCount: Limits the total number of children in the collection.
  • Relation: Custom relation rule between child and collection, via a function.
  • BeforeCheckOut: Custom rule to block removal (check-out) of an item.
  • Required: Requires a field to be present and non-empty.
  • Pattern: Requires a field to match a given regular expression.
  • MinLength: Requires a string field to have at least a minimum length.
  • MaxLength: Requires a string field to have at most a maximum length.
  • Enum: Requires a field to be one of a set of allowed values.
  • RelationCount: Enforces a minimum/maximum count of children with a specific field value.
  • UniqueGroup: Ensures a combination of fields is unique among all children.
  • DateRange: Requires a date field to be within a specified range.
  • Dependency: Requires another field to be present if a given field has a specific value.

Example Usage

const constraints = [
  { type: ConstraintType.Required, key: 'email' },
  { type: ConstraintType.Pattern, key: 'email', regex: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
  { type: ConstraintType.MinLength, key: 'name', length: 2 },
  { type: ConstraintType.MaxLength, key: 'name', length: 30 },
  { type: ConstraintType.Enum, key: 'role', values: ['admin', 'user'] },
  { type: ConstraintType.Range, key: 'age', min: 18, max: 65 },
  { type: ConstraintType.UniqueGroup, keys: ['email', 'role'] },
  { type: ConstraintType.DateRange, key: 'createdAt', min: '2023-01-01', max: '2025-12-31' },
  { type: ConstraintType.Dependency, key: 'role', value: 'admin', required: 'adminCode' },
  (child, children) => child.role === 'admin' && children.filter(u => u.role === 'admin').length >= 2
    ? 'Maximum 2 admins'
    : null,
];

Error Handling

If a constraint is violated during check-in or check-out, the plugin throws an exception and blocks the operation. You can provide custom error messages for each constraint.

API Reference

createConstraintsPlugin<T>(constraints: Constraint<T>[]): CheckInPlugin<T>

Creates a plugin enforcing the provided constraints.

Constraint Typing

type ConstraintFn<T> = (child: T, children: T[]) => string | null;
type ConstraintObj<T> =
  | { type: ConstraintType.Unique; key: keyof T; message?: string }
  | { type: ConstraintType.MaxCount; count: number; message?: string }
  | { type: ConstraintType.Required; key: keyof T; message?: string }
  | { type: ConstraintType.Pattern; key: keyof T; regex: RegExp; message?: string }
  | { type: ConstraintType.MinLength; key: keyof T; length: number; message?: string }
  | { type: ConstraintType.MaxLength; key: keyof T; length: number; message?: string }
  | { type: ConstraintType.Enum; key: keyof T; values: any[]; message?: string }
  | { type: ConstraintType.Range; key: keyof T; min: number; max: number; message?: string }
  | { type: ConstraintType.UniqueGroup; keys: (keyof T)[]; message?: string }
  | { type: ConstraintType.DateRange; key: keyof T; min: string | Date; max: string | Date; message?: string }
  | { type: ConstraintType.Dependency; key: keyof T; value: any; required: keyof T; message?: string }
  | { type: ConstraintType.Relation; rule: ConstraintFn<T>; message?: string }
  | { type: ConstraintType.BeforeCheckOut; rule: ConstraintFn<T>; message?: string }
  | { type: ConstraintType.Custom; fn: (child: T, children: T[]) => string | null | Promise<string | null>; message?: string }
  // ...and more

How Constraints and Validation Plugins Work Together

The Constraints Plugin and the Validation Plugin are designed to be complementary:

  • Validation Plugin focuses on validating individual item fields (e.g., required fields, custom validation logic, format checks) before check-in. It is ideal for form validation and ensuring each item meets basic requirements.
  • Constraints Plugin enforces rules that involve relationships between multiple items, collection-wide limits, dependencies, uniqueness, and business logic that spans across the desk.

Typical usage:

  • Use the Validation Plugin to ensure each item is well-formed and valid on its own.
  • Use the Constraints Plugin to enforce rules that depend on the state of the whole desk or relationships between items (e.g., no duplicate emails, max 2 admins, dependencies between fields).

Both plugins can be used together in the same desk for robust data integrity and business rule enforcement.

Use Cases

  • Enforce uniqueness: No duplicate emails, usernames, etc.
  • Limit cardinality: Restrict the number of items (e.g., max 5 members).
  • Business dependencies: Ensure required relationships between items.
  • Custom business logic: Any rule expressible as a function.
  • Field validation: Patterns, ranges, enums, length, etc.

Examples

See the Business Rules Example for a complete working implementation.