В основе всей системы схем в Joi лежит универсальный тип
any. Он представляет максимально абстрактную схему, которая
допускает значения любого типа без применения ограничений, если они не
заданы явно.
Схема Joi.any() является базовым строительным блоком для
всех остальных типов (string, number,
object и т.д.), поскольку они наследуют общую логику
валидации от него.
При использовании Joi.any():
null, undefined)Пример:
import Joi from 'joi';
const schema = Joi.any();
schema.validate(123); // valid
schema.validate("text"); // valid
schema.validate({ a: 1 }); // valid
schema.validate(null); // valid
Такое поведение делает any нейтральной точкой входа,
когда ограничения либо отсутствуют, либо формируются динамически через
дополнительные методы.
Несмотря на «всеядность», any поддерживает систему
модификаторов:
const schema = Joi.any()
.valid('A', 'B')
.required();
Здесь базовая универсальность сужается до конкретных допустимых значений.
Также доступны:
allow() — расширение допустимых значенийinvalid() — исключение значенийcustom() — пользовательская логика проверкиrules() — внутренние расширения типовТаким образом, any выступает не как отсутствие схемы, а
как гибкий контейнер для правил.
В объектах any часто применяется для динамических или
слабо типизированных полей:
const schema = Joi.object({
metadata: Joi.any(),
timestamp: Joi.date(),
});
Поле metadata может содержать произвольную структуру, не
нарушая валидацию объекта.
Ключевая особенность any — отсутствие навязанных
ограничений. Однако это не означает полное отсутствие логики
проверки.
По умолчанию any:
Но при добавлении модификаторов поведение изменяется строго детерминированно.
Противоположным по смыслу инструментом является
Joi.forbidden(). Эта схема обозначает абсолютный запрет на
наличие поля или значения.
С технической точки зрения это специализированная форма
any, настроенная на всегда невалидное состояние при наличии
данных.
const schema = Joi.object({
token: Joi.forbidden()
});
Любое присутствие token в объекте приведёт к ошибке
валидации.
undefined также трактуется как отсутствие, но наличие
ключа уже нарушает правилоПример:
const schema = Joi.object({
secret: Joi.forbidden()
});
schema.validate({});
// valid
schema.validate({ secret: '123' });
// error
Несмотря на общую принадлежность к базовому типу, семантика противоположна:
| Схема | Поведение |
|---|---|
Joi.any() |
разрешает всё |
Joi.forbidden() |
запрещает любое присутствие |
Фактически:
any задаёт отсутствие ограниченийforbidden задаёт максимальное ограничение (полный
запрет)Joi.forbidden() реализуется как частный случай
any с модификатором, который всегда возвращает ошибку при
наличии значения.
Эквивалентная конструкция:
Joi.any().forbidden()
Однако использование прямого Joi.forbidden() является
более выразительным и семантически точным.
В системах, где структура заранее неизвестна:
const schema = Joi.object({
payload: Joi.any()
});
Поле payload может содержать результат стороннего API
или произвольные данные.
При передаче данных без изменений:
const schema = Joi.object({
data: Joi.any().required()
});
Используется как «прозрачный контейнер».
Когда формат не контролируется:
const schema = Joi.object({
password: Joi.string().required(),
_internal: Joi.forbidden()
});
Поле _internal исключается из входных данных независимо
от его наличия.
При строгом контроле входных данных:
const schema = Joi.object({
role: Joi.string(),
adminOverride: Joi.forbidden()
});
Любые попытки передать adminOverride блокируются на
уровне валидации.
При эволюции API:
const schema = Joi.object({
newField: Joi.string(),
oldField: Joi.forbidden()
});
Позволяет явно обозначить устаревшие поля как недопустимые.
const schema = Joi.object({
config: Joi.object({
raw: Joi.any()
})
});
Поле raw допускает любые значения, но структура
config остаётся ограниченной.
const schema = Joi.object({
config: Joi.object({
debug: Joi.forbidden()
})
});
Даже наличие ключа debug нарушает схему объекта
config.
Хотя any допускает любые значения, поведение может
изменяться через дополнительные модификаторы:
Joi.any().allow(null)
Позволяет явно расширить допустимые значения.
Joi.any().strip()
Удаляет поле из результата после валидации, даже если оно допустимо.
В сочетании:
Joi.any().allow('x').strip()
значение может быть валидным, но будет исключено из результата.
При нарушении forbidden возвращается ошибка с типом:
any.unknown (в зависимости от конфигурации Joi)Пример структуры ошибки:
{
"message": "\"secret\" is not allowed",
"path": ["secret"],
"type": "object.unknown"
}
Важно различие:
undefined → может считаться нарушением в
зависимости от настройки presenceconst schema = Joi.object({
token: Joi.forbidden()
});
schema.validate({});
// valid
any играет ключевую архитектурную роль:
required,
optional, valid, invalid)Все специализированные типы в Joi в конечном счёте опираются на его поведение.
Joi.any().valid(1, 2)
Joi.any().invalid(3)
Joi.any().required()
Joi.any().forbidden()
Каждый метод модифицирует базовую универсальность any,
но forbidden полностью блокирует допустимость независимо от
других правил.
При конфликте правил:
forbidden имеет приоритетЛогически поведение можно интерпретировать так:
any → «нет ограничений»forbidden → «полное отрицание присутствия»Эти два состояния образуют крайние точки системы валидации Joi, между которыми располагаются все остальные типы и правила.