При работе с пользовательским вводом в JavaScript одной из ключевых задач становится проверка корректности данных до их дальнейшей обработки. В приложениях средней и высокой сложности ручные проверки быстро превращаются в громоздкий и трудно поддерживаемый код. Именно здесь используются библиотеки схемной валидации, среди которых особенно часто применяется Yup.
В контексте простых сценариев валидации важно понимать границу между двумя подходами: строковыми валидаторами наподобие validator.js и схемными решениями вроде Yup. Первый вариант хорошо подходит для точечных проверок (например, email или URL), второй — для описания целых структур данных.
Библиотеки наподобие validator.js ориентированы на проверку отдельных значений:
import validator from 'validator';
validator.isEmail('test@mail.com'); // true
validator.isLength('hello', { min: 3, max: 10 }); // true
Такой подход удобен в случаях, когда необходимо быстро проверить одно поле. Однако при увеличении количества полей и правил код начинает расползаться:
function validateUser(data) {
const errors = {};
if (!validator.isEmail(data.email)) {
errors.email = 'Некорректный email';
}
if (!validator.isLength(data.password, { min: 6 })) {
errors.password = 'Слишком короткий пароль';
}
if (!validator.isLength(data.name, { min: 2 })) {
errors.name = 'Имя слишком короткое';
}
return errors;
}
Подобный код сложно расширять, тестировать и поддерживать. Логика проверки смешивается с бизнес-логикой.
Схемная валидация решает проблему разделения структуры данных и правил проверки. В Yup валидация описывается как декларативная схема:
import * as yup from 'yup';
const userSchema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(6).required(),
name: yup.string().min(2).required(),
});
Вместо множества условий создаётся единая структура, описывающая требования к данным.
Проверка становится централизованной:
userSchema.validate(data)
.then(validData => {
console.log('OK', validData);
})
.catch(err => {
console.log(err.errors);
});
В простых случаях Yup используется как замена ручным проверкам строк и чисел:
yup.string()
.required()
.min(3)
.max(50)
yup.number()
.required()
.positive()
.integer()
yup.boolean()
.oneOf([true], 'Значение должно быть true')
yup.date()
.required()
.min(new Date(2020, 0, 1))
Каждое правило добавляется как часть цепочки, формируя читаемую декларацию.
Хотя Yup ориентирован на схемы, он поддерживает и точечные проверки:
yup.string().email().validate('test@mail.com')
.then(value => console.log(value))
.catch(err => console.log(err.errors));
Это приближает его по использованию к validator.js, но сохраняет единый стиль API.
Наиболее частый случай — валидация форм. Даже минимальная форма регистрации выигрывает от схемного подхода:
const loginSchema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(6).required(),
});
Обработка ошибки:
try {
await loginSchema.validate(formData, { abortEarly: false });
} catch (err) {
const errors = err.inner.reduce((acc, curr) => {
acc[curr.path] = curr.message;
return acc;
}, {});
}
Параметр abortEarly: false позволяет собрать все ошибки
сразу, что важно для UX простых форм.
Даже в простых сценариях возникает необходимость переиспользования правил:
const emailRule = yup.string().email().required();
const schema = yup.object({
primaryEmail: emailRule,
backupEmail: emailRule.notRequired(),
});
Такой подход уменьшает дублирование и повышает согласованность логики.
Даже в простых кейсах иногда требуется минимальная логика зависимости полей:
const schema = yup.object({
password: yup.string().min(6).required(),
confirmPassword: yup.string()
.oneOf([yup.ref('password')], 'Пароли не совпадают')
.required(),
});
Это уже выходит за рамки validator.js, где подобные зависимости реализуются вручную.
По умолчанию Yup возвращает массив ошибок, что удобно для интерфейсов:
err.errors // ['email is a required field', 'password is too short']
Но для простых приложений чаще требуется структура вида «поле → сообщение»:
function formatErrors(err) {
return err.inner.reduce((acc, e) => {
acc[e.path] = e.message;
return acc;
}, {});
}
Такой формат упрощает интеграцию с формами и UI-библиотеками.
Даже в простых сценариях иногда требуется проверка уникальности значения:
const schema = yup.object({
username: yup.string()
.test('check-username', 'Имя занято', async value => {
const res = await fetch(`/api/check?username=${value}`);
return res.ok;
}),
});
Хотя это уже ближе к продвинутым сценариям, сам механизм органично встроен в базовую модель Yup.
В небольших приложениях схема обычно ограничивается одной сущностью:
Во всех этих случаях схема остаётся компактной, но уже обеспечивает единый стандарт проверки данных, избавляя от необходимости писать набор изолированных функций.
Даже при минимальной сложности проекта схемный подход обеспечивает:
В отличие от наборов функций validator.js, схема становится самостоятельной частью архитектуры приложения, а не вспомогательным инструментом.