Validator.js для простых случаев

При работе с пользовательским вводом в JavaScript одной из ключевых задач становится проверка корректности данных до их дальнейшей обработки. В приложениях средней и высокой сложности ручные проверки быстро превращаются в громоздкий и трудно поддерживаемый код. Именно здесь используются библиотеки схемной валидации, среди которых особенно часто применяется Yup.

В контексте простых сценариев валидации важно понимать границу между двумя подходами: строковыми валидаторами наподобие validator.js и схемными решениями вроде Yup. Первый вариант хорошо подходит для точечных проверок (например, email или URL), второй — для описания целых структур данных.


Строковая валидация и ограниченность Validator.js

Библиотеки наподобие 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

Схемная валидация решает проблему разделения структуры данных и правил проверки. В 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, схема становится самостоятельной частью архитектуры приложения, а не вспомогательным инструментом.