Валидация данных в JavaScript (Node.js): Joi, Yup, Zod

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Валидация данных в Node.js: Joi, Yup и Zod — выбираем правильный инструмент

В современной веб-разработке на Node.js валидация данных — это не просто хорошая практика, а необходимость. Без неё ваш сервер рискует получить некорректные, вредоносные или просто неожиданные данные от клиента. Это может привести к ошибкам в логике приложения, уязвимостям (SQL-инъекции, XSS) и нестабильной работе. В этой статье мы разберём три самые популярные библиотеки для валидации данных в среде Node.js: Joi, Yup и Zod. Вы узнаете их особенности, преимущества и научитесь применять на практике.

Зачем нужна валидация на сервере?

Многие разработчики полагаются только на фронтенд-валидацию (например, с помощью HTML5-атрибутов). Это ошибка. Клиентскую валидацию легко обойти (отключив JavaScript или отправив запрос через Postman/curl). Серверная валидация — это последний рубеж защиты. Она гарантирует, что в вашу бизнес-логику и базу данных попадут только данные, соответствующие строгим требованиям: правильный тип, длина, формат (email, URL), обязательность полей и т.д.

1. Joi — проверенный ветеран

Joi (часто используется с фреймворком Hapi.js, но отлично работает и с Express/Koa) — одна из самых мощных и гибких библиотек. Она предоставляет богатый API для описания схем и сообщений об ошибках.

Установка:

npm install joi

Пример использования Joi для валидации регистрации пользователя:

const Joi = require('joi');

const schema = Joi.object({
    username: Joi.string()
        .alphanum()
        .min(3)
        .max(30)
        .required()
        .messages({
            'string.min': 'Имя пользователя должно быть минимум {#limit} символа',
            'string.max': 'Имя пользователя должно быть максимум {#limit} символов'
        }),
    password: Joi.string()
        .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$'))
        .required(),
    email: Joi.string()
        .email({ minDomainSegments: 2, tlds: { allow: ['com', 'net', 'ru'] } })
        .required(),
    birth_year: Joi.number()
        .integer()
        .min(1900)
        .max(2023)
});

const validateUser = (data) => {
    const { error, value } = schema.validate(data, { abortEarly: false });
    if (error) {
        // Возвращаем все ошибки
        return { valid: false, errors: error.details.map(d => d.message) };
    }
    return { valid: true, data: value };
};

// Пример вызова
const result = validateUser({
    username: 'jo',
    password: '123',
    email: 'test@test',
    birth_year: 1800
});
console.log(result.errors);
// Вывод: ['Имя пользователя должно быть минимум 3 символа', '"password" length must be at least 3 characters long', '"email" must be a valid email', '"birth_year" must be less than or equal to 2023']

Плюсы Joi:

  • Огромное количество встроенных валидаторов (email, URL, UUID, credit card, phone и т.д.).
  • Поддержка условной валидации (when, switch).
  • Отличные сообщения об ошибках с возможностью перевода и кастомизации.
  • Детальная документация.

Минусы:

  • Размер библиотеки (около 60 КБ в сжатом виде).
  • API может показаться громоздким для простых схем.
  • Сложная интеграция с TypeScript (требуются дополнительные типы).

2. Yup — простота и композиция

Yup изначально создавался для валидации форм на React (в связке с Formik), но отлично работает и на сервере. Его главное преимущество — лаконичный и читаемый синтаксис, напоминающий mongoose-схемы.

Установка:

npm install yup

Пример валидации массива объектов с помощью Yup:

const yup = require('yup');

const productSchema = yup.object({
    name: yup.string().required('Название обязательно').min(2, 'Минимум 2 символа'),
    price: yup.number().required('Цена обязательна').positive('Цена должна быть положительной'),
    tags: yup.array().of(yup.string().oneOf(['electronics', 'clothes', 'food'])).min(1, 'Выберите хотя бы один тег'),
    inStock: yup.boolean().default(true)
});

const validateProducts = async (productsArray) => {
    try {
        // Валидируем каждый элемент массива
        const validated = await Promise.all(productsArray.map(p =>                    

Рекомендаций