Объекты: object()

object()

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

Основная идея заключается в том, что объект рассматривается как набор ключей, каждый из которых может иметь собственную схему валидации. Это позволяет строго контролировать структуру входных данных, включая вложенные объекты и сложные иерархии.


Схема объекта создаётся через вызов:

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

В этом случае описывается структура, где объект должен содержать поля name и age, соответствующие указанным типам.

Joi по умолчанию разрешает наличие дополнительных полей, если это не ограничено явно.


Явное управление ключами объекта

Каждое свойство объекта описывается как самостоятельная схема:

const schema = Joi.object({
  id: Joi.string().alphanum().required(),
  email: Joi.string().email().required(),
  isActive: Joi.boolean().default(true)
});

Здесь:

  • id должен быть строкой, содержащей только буквы и цифры
  • email должен соответствовать формату email
  • isActive необязателен и получает значение по умолчанию

Обязательные и необязательные поля

Joi различает обязательные и необязательные поля через методы .required() и .optional().

const schema = Joi.object({
  username: Joi.string().required(),
  nickname: Joi.string().optional()
});

По умолчанию поля считаются необязательными, если не указано обратное.


Контроль дополнительных полей: unknown()

Одним из ключевых механизмов управления структурой объекта является метод .unknown().

const schema = Joi.object({
  name: Joi.string()
}).unknown(false);

Поведение:

  • unknown(false) — запрещает любые поля, не описанные в схеме
  • unknown(true) — разрешает дополнительные поля (поведение по умолчанию)

Пример строгой схемы:

const schema = Joi.object({
  id: Joi.number()
}).unknown(false);

Любые лишние ключи приведут к ошибке валидации.


Ограничение структуры через keys()

Метод .keys() используется для явного задания структуры объекта после его создания.

const schema = Joi.object().keys({
  title: Joi.string(),
  content: Joi.string()
});

Этот вариант эквивалентен передаче объекта в Joi.object({...}), но удобен при динамическом построении схем.


Вложенные объекты

Joi поддерживает рекурсивное описание объектов:

const schema = Joi.object({
  user: Joi.object({
    id: Joi.number().required(),
    profile: Joi.object({
      firstName: Joi.string(),
      lastName: Joi.string()
    })
  })
});

Вложенные структуры позволяют описывать сложные модели данных, включая API-ответы и конфигурации.


Проверка ключей объекта

Joi позволяет ограничивать количество ключей в объекте:

const schema = Joi.object({
  a: Joi.string(),
  b: Joi.string()
}).min(1).max(2);

Методы:

  • .min(n) — минимальное количество ключей
  • .max(n) — максимальное количество ключей
  • .length(n) — строго фиксированное количество ключей

Логические зависимости между ключами

and()

Все указанные ключи должны присутствовать одновременно:

Joi.object({
  a: Joi.string(),
  b: Joi.string(),
  c: Joi.string()
}).and('a', 'b');

Если присутствует a, то обязательно должен быть b, и наоборот.


or()

Должен присутствовать хотя бы один из ключей:

Joi.object({
  a: Joi.string(),
  b: Joi.string()
}).or('a', 'b');

xor()

Присутствует ровно один из указанных ключей:

Joi.object({
  a: Joi.string(),
  b: Joi.string()
}).xor('a', 'b');

oxor()

Эксклюзивный вариант, где допускается не более одного из ключей:

Joi.object({
  a: Joi.string(),
  b: Joi.string(),
  c: Joi.string()
}).oxor('a', 'b', 'c');

nand()

Запрещает одновременное присутствие ключей:

Joi.object({
  a: Joi.string(),
  b: Joi.string()
}).nand('a', 'b');

Работа с шаблонами ключей: pattern()

Метод .pattern() позволяет валидировать динамические ключи:

const schema = Joi.object({
  [Joi.string().pattern(/^[a-z]+$/)]: Joi.number()
});

Также используется альтернативная форма:

const schema = Joi.object().pattern(
  /^[a-z]+$/,
  Joi.number()
);

Это полезно для объектов с неизвестной заранее структурой, например:

{
  "a": 1,
  "b": 2,
  "c": 3
}

Переименование ключей: rename()

Joi позволяет изменять структуру входного объекта до валидации.

const schema = Joi.object({
  firstName: Joi.string()
}).rename('first_name', 'firstName');

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

.rename('oldKey', 'newKey', {
  ignoreUndefined: true,
  override: true
});
  • override — заменяет существующее значение
  • ignoreUndefined — игнорирует undefined

Изменение схемы: fork()

Метод .fork() позволяет модифицировать поведение отдельных ключей без переписывания всей схемы.

const baseSchema = Joi.object({
  password: Joi.string(),
  email: Joi.string().email()
});

const strictSchema = baseSchema.fork(['password'], (schema) =>
  schema.required()
);

Это позволяет создавать вариации одной и той же модели данных.


Значения по умолчанию

Объекты могут иметь значения по умолчанию:

const schema = Joi.object({
  role: Joi.string().default('user'),
  active: Joi.boolean().default(true)
});

Если поле отсутствует, оно автоматически добавляется при валидации.


Нормализация объекта через preferences

Метод .prefs() задаёт поведение валидации:

const schema = Joi.object({
  name: Joi.string()
}).prefs({
  abortEarly: false,
  stripUnknown: true
});

Основные параметры:

  • abortEarly: false — собирает все ошибки, а не останавливается на первой
  • stripUnknown: true — удаляет лишние поля
  • convert: true — автоматически приводит типы

Очистка объекта: stripUnknown

Удаление неописанных полей часто используется в API:

const schema = Joi.object({
  id: Joi.number()
}).unknown(false).prefs({
  stripUnknown: true
});

Результат — объект содержит только валидированные ключи.


Глубокая валидация и рекурсия

Joi обрабатывает вложенные структуры рекурсивно:

const schema = Joi.object({
  config: Joi.object({
    settings: Joi.object({
      theme: Joi.string(),
      layout: Joi.string()
    })
  })
});

Каждый уровень проходит собственную проверку по своей схеме.


Комбинирование схем объектов

Схемы могут объединяться:

const base = Joi.object({
  id: Joi.number()
});

const extended = base.keys({
  name: Joi.string()
});

Также возможно использование .concat():

const schema = base.concat(
  Joi.object({
    name: Joi.string()
  })
);

Валидация и результат

При проверке объекта используется:

const { error, value } = schema.validate(data);
  • error содержит описание ошибок
  • value содержит преобразованные данные

При включённых преобразованиях Joi может:

  • приводить типы (строка → число)
  • добавлять значения по умолчанию
  • удалять лишние поля

Строгая модель данных

Комбинация .required(), .unknown(false) и .stripUnknown(true) формирует строгую схему:

const schema = Joi.object({
  id: Joi.number().required(),
  name: Joi.string().required()
})
.unknown(false)
.prefs({ stripUnknown: true });

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