Instance и конструкторы

В библиотеке валидации Joi ключевым концептом является instance (экземпляр схемы) — объект, который представляет собой конкретное правило валидации для данных определённого типа. Каждый такой экземпляр создаётся через набор конструкторов и фабричных методов и является неизменяемым (immutable) после создания.

Instance в Joi — это не просто структура данных, а полноценная конфигурация правил, которая включает:

  • тип значения (string, number, object, array и т.д.)
  • цепочку правил валидации
  • настройки поведения (required, optional, default)
  • кастомные ограничения и расширения

Пример базового экземпляра:

const Joi = require('joi');

const schema = Joi.string().min(3).max(10).required();

В данном случае schema — это instance, созданный конструктором Joi.string() и модифицированный цепочкой методов.


Конструкторы типов в Joi

Каждый тип данных в Joi создаётся через специализированный конструктор. Эти конструкторы являются фабричными функциями, возвращающими новый экземпляр схемы.

Основные конструкторы

String schema instance

const schema = Joi.string();

Возвращает instance типа StringSchema, который далее может быть расширен:

Joi.string()
  .min(5)
  .max(20)
  .email()
  .required();

Number schema instance

const schema = Joi.number();

Поддерживает числовые ограничения:

Joi.number()
  .integer()
  .positive()
  .min(1)
  .max(100);

Object schema instance

const schema = Joi.object({
  name: Joi.string(),
  age: Joi.number()
});

Объектный instance играет центральную роль в Joi, так как позволяет описывать вложенные структуры.

Array schema instance

const schema = Joi.array().items(Joi.string());

Позволяет задавать ограничения на элементы массива:

Joi.array()
  .items(Joi.number().integer())
  .min(1)
  .max(10);

Внутренняя модель instance

Каждый instance в Joi содержит внутреннее состояние, которое описывает:

  • type — тип схемы
  • rules — список правил
  • flags — поведенческие настройки
  • children — вложенные схемы (для object и array)
  • preferences — глобальные настройки валидации

Пример логической структуры:

{
  type: 'string',
  rules: [
    { name: 'min', args: { limit: 3 } },
    { name: 'max', args: { limit: 10 } }
  ],
  flags: {
    presence: 'required'
  }
}

Иммутабельность instance

Все instance в Joi являются immutable. Это означает, что каждый вызов метода не изменяет текущий объект, а возвращает новый экземпляр схемы.

const base = Joi.string();
const extended = base.min(5);

console.log(base === extended); // false

Такой подход обеспечивает:

  • безопасное переиспользование схем
  • отсутствие побочных эффектов
  • предсказуемость валидации

Цепочки конструкторов и паттерн композиции

Instance в Joi поддерживает fluent API, основанный на цепочках вызовов.

const passwordSchema = Joi.string()
  .min(8)
  .pattern(/[A-Z]/)
  .pattern(/[0-9]/)
  .required();

Каждый метод возвращает новый instance, расширяющий предыдущий.

Композиция позволяет создавать базовые схемы и переиспользовать их:

const baseString = Joi.string().trim().lowercase();

const username = baseString.min(3).max(30);
const tag = baseString.max(15);

Клонирование instance

При необходимости Joi создаёт копии схем через внутренний механизм clone.

const schema1 = Joi.string().min(3);
const schema2 = schema1.min(5);

Здесь schema2 — это не модификация schema1, а новый instance с обновлёнными правилами.

Клонирование используется при:

  • добавлении правил
  • расширении схем
  • создании вложенных объектов

Конструкторы объектов (object instance)

Object instance является наиболее сложным типом в Joi.

const userSchema = Joi.object({
  id: Joi.number().integer(),
  name: Joi.string(),
  email: Joi.string().email()
});

Внутри такой instance хранит:

  • карту ключей и их схем
  • стратегию обработки неизвестных ключей
  • флаги строгой/нестрогой проверки

Дополнительные методы:

Joi.object()
  .keys({...})
  .unknown(false)
  .required();

Динамическое создание instance

Instance может создаваться динамически в зависимости от условий:

function createSchema(isStrict) {
  let schema = Joi.object({
    name: Joi.string(),
    age: Joi.number()
  });

  if (isStrict) {
    schema = schema.required().unknown(false);
  }

  return schema;
}

Расширение instance через custom

Joi позволяет создавать расширенные instance через extend API.

const custom = Joi.extend((joi) => ({
  type: 'evenNumber',
  base: joi.number(),
  validate(value, helpers) {
    if (value % 2 !== 0) {
      return { value, errors: helpers.error('number.even') };
    }
  }
}));

Здесь создаётся новый тип instance, основанный на number.


Наследование и базовые instance

Часто используется подход создания базового instance, от которого строятся остальные схемы.

const base = Joi.string().trim().lowercase();

const email = base.email();
const username = base.alphanum().min(3);

Это позволяет централизовать общие правила.


Поведение instance при валидации

Каждый instance содержит встроенную логику выполнения проверки:

  • последовательный проход по правилам
  • применение преобразований (cast, trim, normalize)
  • возврат результата или ошибок
const schema = Joi.string().min(3);

const result = schema.validate('ab');

Instance выступает как компилированное описание валидатора.


Компиляция instance

Перед валидацией Joi может компилировать schema instance в оптимизированную структуру:

  • упорядочивание правил
  • объединение проверок
  • оптимизация вложенных схем

Это снижает стоимость повторных вызовов validate.


Повторное использование instance

Instance можно безопасно использовать многократно:

const schema = Joi.number().integer().positive();

schema.validate(10);
schema.validate(20);

Так как instance неизменяем, он подходит для глобальных конфигураций.


Вложенные instance

Joi активно использует вложенные схемы:

const schema = Joi.object({
  profile: Joi.object({
    age: Joi.number().min(18)
  })
});

Здесь каждый уровень — отдельный instance со своей логикой.


Связь instance и runtime-валидации

Instance в Joi — это декларативное описание, которое преобразуется в runtime-алгоритм проверки. В отличие от ручных проверок, instance:

  • описывает правила заранее
  • не содержит логики выполнения в явном виде
  • компилируется в валидатор при необходимости

Это отделяет описание данных от их обработки и делает систему масштабируемой.