Failover в Joi реализуется через сочетание механизмов альтернативных схем, значений по умолчанию, условной валидации и постобработки результата. Подход строится вокруг идеи: при невозможности корректно распознать или принять входные данные система должна либо заменить их безопасным значением, либо переключиться на альтернативную схему разбора.
Основной уровень отказоустойчивости в Joi обеспечивается методом
default(), который подставляет значение при отсутствии
входных данных или при их undefined.
import Joi from 'joi';
const schema = Joi.object({
role: Joi.string().default('guest'),
retries: Joi.number().integer().min(0).default(3),
enabled: Joi.boolean().default(true)
});
const result = schema.validate({});
// role: 'guest', retries: 3, enabled: true
Значения по умолчанию работают как первый уровень failover: если данных нет, система переходит на заранее заданную безопасную конфигурацию.
Важно учитывать, что default() не срабатывает при
невалидных значениях — только при отсутствии или undefined,
что определяет его как частичный механизм отказоустойчивости.
Полноценный failover-механизм реализуется через
Joi.alternatives(). Он позволяет определить несколько схем,
которые проверяются последовательно до первого успешного совпадения.
const schema = Joi.alternatives().try(
Joi.string().email(),
Joi.string().uri(),
Joi.string().pattern(/^[a-zA-Z0-9_-]+$/)
);
schema.validate('example@example.com');
schema.validate('https://site.com');
schema.validate('user_123');
Поведение try():
Это основной механизм структурного failover, когда вход может иметь несколько допустимых форматов.
Failover часто дополняется преобразованием входа перед проверкой:
const schema = Joi.alternatives().try(
Joi.number(),
Joi.string().pattern(/^\d+$/).custom((value) => parseInt(value, 10))
);
В этом случае строковое значение "123" автоматически
приводится к числу, если первая схема не проходит.
Подобный подход используется для:
Метод when() позволяет переключать схему в зависимости
от состояния других полей, реализуя контекстный failover.
const schema = Joi.object({
type: Joi.string().valid('email', 'phone'),
value: Joi.when('type', {
is: 'email',
then: Joi.string().email(),
otherwise: Joi.string().pattern(/^\+?[0-9]{10,15}$/)
})
});
Здесь failover происходит не при ошибке, а при изменении контекста: схема адаптируется к типу данных.
Сложные системы используют вложенные альтернативы для каскадного перехода:
const schema = Joi.alternatives().try(
Joi.object({
source: Joi.string().valid('internal'),
id: Joi.number().integer()
}),
Joi.object({
source: Joi.string().valid('external'),
uuid: Joi.string().guid()
}),
Joi.object({
source: Joi.string().valid('legacy'),
legacyId: Joi.string()
})
);
Каждая схема представляет отдельный канал получения данных. Failover происходит между источниками.
Когда стандартных средств недостаточно, применяется
custom() для ручной логики переключения.
const schema = Joi.any().custom((value, helpers) => {
if (typeof value === 'string' && value.startsWith('{')) {
try {
return JSON.parse(value);
} catch (e) {
return helpers.error('any.invalid');
}
}
return value;
});
Такой подход позволяет реализовать:
Failover часто включает не только проверку, но и нормализацию:
const schema = Joi.string()
.trim()
.lowercase()
.empty('')
.default('unknown');
Здесь реализуется цепочка:
Подобная комбинация снижает вероятность ошибок на уровне данных.
Порядок схем в try() определяет стратегию failover:
const schema = Joi.alternatives().try(
Joi.number().strict(),
Joi.number().unsafe(),
Joi.string().pattern(/^\d+$/)
);
Это позволяет контролировать деградацию качества данных.
Валидация объектов часто требует комбинированного подхода:
const schema = Joi.object({
config: Joi.alternatives().try(
Joi.object({
mode: Joi.string().valid('safe'),
timeout: Joi.number().default(1000)
}),
Joi.object({
mode: Joi.string().valid('fast'),
timeout: Joi.number().default(100)
})
)
});
Каждый режим содержит собственные параметры, а failover переключает конфигурацию целиком.
Joi не выполняет автоматическое подавление ошибок, но их можно
преобразовывать в fallback-значения через validate:
const { error, value } = schema.validate(input);
const safeValue = error
? { mode: 'default', value: null }
: value;
Это внешний слой failover-логики, применяемый при интеграции с бизнес-логикой.
Failover в Joi не отменяет валидацию, а дополняет её:
const schema = Joi.alternatives().try(
Joi.object({
version: Joi.number().valid(2),
data: Joi.object()
}),
Joi.object({
version: Joi.number().valid(1),
payload: Joi.string()
})
);
Такая структура позволяет поддерживать несколько версий API без потери совместимости.
На практике выделяются несколько подходов:
try()when()custom()default()Комбинация этих механизмов формирует устойчивую систему обработки данных, в которой ошибки входа не приводят к отказу всей логики, а переводятся в управляемое состояние.