Валидация перед сохранением

Валидация перед сохранением — процесс проверки данных до их записи в базу данных, файл, очередь сообщений или внешний API. Основная цель — гарантировать корректность, безопасность и согласованность информации.

Библиотека Validator.js предоставляет набор готовых функций для проверки строковых значений: email, URL, UUID, IP-адресов, чисел, дат, JSON, банковских карт и множества других форматов.

Типичный поток обработки данных:

Получение данных -> Очистка -> Валидация -> Сохранение

Без предварительной проверки возможны:

  • запись некорректных данных;
  • ошибки SQL и ORM;
  • нарушения бизнес-логики;
  • XSS и инъекции;
  • повреждение структуры данных;
  • дублирование информации.

Установка Validator.js

Установка через npm

npm install validator

Подключение

CommonJS:

const validator = require('validator');

ES Modules:

import validator from 'validator';

Проверка обязательных полей

Перед сохранением данные обычно проверяются на наличие значения.

Проверка пустой строки

import validator from 'validator';

const name = '';

if (validator.isEmpty(name)) {
    console.log('Имя обязательно');
}

Игнорирование пробелов

const value = '     ';

if (validator.isEmpty(value.trim())) {
    console.log('Строка пустая');
}

Проверка нескольких полей

function validateRequired(data) {
    const errors = {};

    if (validator.isEmpty(data.name.trim())) {
        errors.name = 'Введите имя';
    }

    if (validator.isEmpty(data.email.trim())) {
        errors.email = 'Введите email';
    }

    return errors;
}

Валидация email перед сохранением

Email — одно из самых распространённых полей.

Базовая проверка

const email = 'admin@example.com';

if (validator.isEmail(email)) {
    console.log('Email корректен');
}

Проверка с дополнительными параметрами

validator.isEmail(email, {
    allow_utf8_local_part: false,
    require_tld: true
});

Проверка email в форме регистрации

function validateUser(user) {
    const errors = {};

    if (!validator.isEmail(user.email)) {
        errors.email = 'Некорректный email';
    }

    return errors;
}

Валидация паролей

Validator.js не содержит отдельного метода проверки сложности пароля, но предоставляет инструменты для построения правил.

Минимальная длина

const password = 'qwerty';

if (!validator.isLength(password, { min: 8 })) {
    console.log('Минимум 8 символов');
}

Проверка сложности через регулярное выражение

const strongPassword =
    /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$/;

if (!strongPassword.test(password)) {
    console.log('Слабый пароль');
}

Комплексная проверка

function validatePassword(password) {
    const errors = [];

    if (!validator.isLength(password, { min: 8 })) {
        errors.push('Минимум 8 символов');
    }

    if (!/[A-Z]/.test(password)) {
        errors.push('Добавьте заглавную букву');
    }

    if (!/\d/.test(password)) {
        errors.push('Добавьте цифру');
    }

    return errors;
}

Проверка числовых данных

Валидация целых чисел

validator.isInt('100');

Проверка диапазона

validator.isInt('25', {
    min: 18,
    max: 60
});

Проверка дробных чисел

validator.isFloat('10.55');

Проверка цены товара

function validatePrice(price) {
    return validator.isFloat(price.toString(), {
        min: 0
    });
}

Проверка дат перед сохранением

Проверка формата даты

validator.isDate('2025-05-10');

Проверка ISO-формата

validator.isISO8601('2025-05-10T12:00:00Z');

Валидация даты рождения

function validateBirthDate(date) {
    if (!validator.isDate(date)) {
        return false;
    }

    const birthday = new Date(date);
    const now = new Date();

    return birthday < now;
}

Валидация URL

Проверка адреса сайта

validator.isURL('https://example.com');

Ограничение протоколов

validator.isURL('https://example.com', {
    protocols: ['https'],
    require_protocol: true
});

Проверка ссылки в модели

function validateWebsite(url) {
    return validator.isURL(url, {
        require_protocol: true
    });
}

Валидация UUID

UUID часто используется как идентификатор записей.

Проверка UUID

validator.isUUID(
    '550e8400-e29b-41d4-a716-446655440000'
);

Проверка версии UUID

validator.isUUID(uuid, 4);

Проверка JSON перед сохранением

Валидация JSON-строки

validator.isJSON('{"name":"Alex"}');

Проверка конфигурации

function validateConfig(config) {
    if (!validator.isJSON(config)) {
        throw new Error('Некорректный JSON');
    }

    return JSON.parse(config);
}

Валидация IP-адресов

IPv4

validator.isIP('192.168.1.1', 4);

IPv6

validator.isIP('2001:db8::1', 6);

Проверка длины строк

Минимальная и максимальная длина

validator.isLength(username, {
    min: 3,
    max: 20
});

Проверка описания товара

function validateDescription(text) {
    return validator.isLength(text, {
        min: 10,
        max: 500
    });
}

Очистка данных перед сохранением

Validator.js умеет не только проверять, но и очищать данные.


Удаление пробелов

trim

const value = validator.trim('   hello   ');

console.log(value);

Результат:

hello

Экранирование HTML

escape

const text = '<script>alert(1)</script>';

const safe = validator.escape(text);

console.log(safe);

Результат:

&lt;script&gt;alert(1)&lt;/script&gt;

Нормализация email

const email =
    validator.normalizeEmail('Admin@Example.COM');

console.log(email);

Результат:

admin@example.com

Удаление лишних символов

whitelist

validator.whitelist('phone: +7(777)-123', '0-9');

Результат:

7777123

Комплексная валидация объекта

Полная проверка пользователя

import validator from 'validator';

function validateUser(user) {
    const errors = {};

    if (
        validator.isEmpty(user.name.trim())
    ) {
        errors.name = 'Имя обязательно';
    }

    if (
        !validator.isLength(user.name, {
            min: 2,
            max: 50
        })
    ) {
        errors.name =
            'Имя должно быть от 2 до 50 символов';
    }

    if (
        !validator.isEmail(user.email)
    ) {
        errors.email =
            'Некорректный email';
    }

    if (
        !validator.isLength(user.password, {
            min: 8
        })
    ) {
        errors.password =
            'Минимум 8 символов';
    }

    if (
        !validator.isInt(user.age.toString(), {
            min: 18
        })
    ) {
        errors.age =
            'Возраст должен быть больше 18';
    }

    return {
        valid: Object.keys(errors).length === 0,
        errors
    };
}

Валидация перед записью в MongoDB

Пример с Mongoose

import validator from 'validator';

const userSchema = new mongoose.Schema({
    email: {
        type: String,
        validate(value) {
            if (!validator.isEmail(value)) {
                throw new Error('Некорректный email');
            }
        }
    }
});

Валидация перед записью в PostgreSQL

Проверка данных до INSERT

async function createUser(data) {

    if (!validator.isEmail(data.email)) {
        throw new Error('Некорректный email');
    }

    if (!validator.isLength(data.name, {
        min: 2
    })) {
        throw new Error('Слишком короткое имя');
    }

    await db.query(
        'INS ERT INTO users(name, email) VALUES($1, $2)',
        [data.name, data.email]
    );
}

Валидация в Express middleware

Middleware проверки пользователя

import validator from 'validator';

function validateUser(req, res, next) {

    const { email, password } = req.body;

    if (!validator.isEmail(email)) {
        return res.status(400).json({
            error: 'Некорректный email'
        });
    }

    if (!validator.isLength(password, {
        min: 8
    })) {
        return res.status(400).json({
            error: 'Слабый пароль'
        });
    }

    next();
}

Санитизация данных перед сохранением

Санитизация — очистка потенциально опасных данных.

Обработка пользовательского ввода

function sanitizeUser(user) {

    return {
        name: validator.escape(
            validator.trim(user.name)
        ),

        email: validator.normalizeEmail(
            user.email
        ),

        about: validator.escape(
            user.about
        )
    };
}

Проверка массивов данных

Validator.js работает со строками, поэтому элементы массива обычно проверяются по отдельности.

Валидация списка email

function validateEmails(emails) {

    return emails.every(email =>
        validator.isEmail(email)
    );
}

Проверка списка ID

function validateIds(ids) {

    return ids.every(id =>
        validator.isUUID(id)
    );
}

Асинхронная валидация перед сохранением

Некоторые проверки требуют обращения к базе данных.

Проверка уникальности email

async function validateUniqueEmail(email) {

    if (!validator.isEmail(email)) {
        return false;
    }

    const user = await User.findOne({ email });

    return !user;
}

Комбинирование Validator.js и собственных правил

Проверка заказа

function validateOrder(order) {

    const errors = [];

    if (!validator.isUUID(order.userId)) {
        errors.push('Некорректный ID пользователя');
    }

    if (!Array.isArray(order.items)) {
        errors.push('Список товаров обязателен');
    }

    if (order.items.length === 0) {
        errors.push('Добавьте товары');
    }

    if (!validator.isFloat(
        order.total.toString(),
        { min: 0 }
    )) {
        errors.push('Некорректная сумма');
    }

    return errors;
}

Создание универсального валидатора

Конфигурируемые правила

const rules = {
    username: val ue =>
        validator.isLength(value, {
            min: 3,
            max: 20
        }),

    email: value =>
        validator.isEmail(value),

    age: value =>
        validator.isInt(value.toString(), {
            min: 18
        })
};

Универсальная функция

function validate(data, rules) {

    const errors = {};

    for (const field in rules) {

        const valid =
            rules[field](data[field]);

        if (!valid) {
            errors[field] =
                `Ошибка поля ${field}`;
        }
    }

    return errors;
}

Обработка ошибок валидации

Централизованный формат ошибок

function validationError(errors) {

    return {
        status: 'error',
        errors
    };
}

Использование

const errors = validateUser(user);

if (Object.keys(errors).length) {

    return res.status(400).json(
        validationError(errors)
    );
}

Частые ошибки при валидации

Проверка undefined

Многие методы Validator.js ожидают строку.

Неправильно:

validator.isEmail(undefined);

Правильно:

validator.isEmail(String(value || ''));

Отсутствие trim

Неправильно:

validator.isEmail(' admin@example.com ');

Правильно:

validator.isEmail(
    validator.trim(email)
);

Доверие только frontend-валидации

Проверка на клиенте удобна для UX, но серверная валидация обязательна.

Нельзя полагаться только на браузер:

<input type="email">

Проверка должна повторяться на сервере:

validator.isEmail(req.body.email)

Рекомендации по архитектуре

Разделение ответственности

Хорошая практика:

controllers/
services/
validators/
models/

Пример структуры

validators/
    userValidator.js
    orderValidator.js
    productValidator.js

Выделение слоя валидации

userValidator.js

import validator from 'validator';

export function validateUser(data) {

    const errors = {};

    if (!validator.isEmail(data.email)) {
        errors.email = 'Некорректный email';
    }

    return errors;
}

controller

import { validateUser } from './validators/userValidator.js';

app.post('/users', async (req, res) => {

    const errors =
        validateUser(req.body);

    if (Object.keys(errors).length) {

        return res.status(400).json({
            errors
        });
    }

    await User.create(req.body);

    res.json({
        success: true
    });
});

Производительность валидации

Validator.js работает быстро, но при больших объёмах данных важно:

  • избегать повторных проверок;
  • валидировать только изменённые поля;
  • выносить тяжёлые проверки в асинхронные процессы;
  • использовать пакетную обработку.

Пример пакетной проверки

const validUsers = users.filter(user =>
    validator.isEmail(user.email)
);

Проверка данных API

Валидация входящего JSON

app.post('/api/products', (req, res) => {

    const { title, price } = req.body;

    if (
        !validator.isLength(title, {
            min: 3
        })
    ) {
        return res.status(400).json({
            error: 'Короткое название'
        });
    }

    if (
        !validator.isFloat(price.toString(), {
            min: 0
        })
    ) {
        return res.status(400).json({
            error: 'Некорректная цена'
        });
    }

    res.json({
        success: true
    });
});

Проверка данных перед сохранением файловых метаданных

Валидация имени файла

function validateFilename(name) {

    return validator.matches(
        name,
        /^[a-zA-Z0-9_\-.]+$/
    );
}

Проверка телефонных номеров

Валидация мобильного номера

validator.isMobilePhone(
    '+77011234567',
    'kk-KZ'
);

Проверка нескольких локалей

validator.isMobilePhone(
    phone,
    ['ru-RU', 'kk-KZ']
);

Проверка slug перед сохранением

URL-friendly строка

validator.isSlug('my-awesome-post');

Проверка банковской карты

Валидация номера карты

validator.isCreditCard('4111111111111111');

Проверка хэшей

Проверка MD5

validator.isHash(hash, 'md5');

Проверка SHA256

validator.isHash(hash, 'sha256');

Проверка MIME-типов

Проверка base64

validator.isBase64(fileContent);

Использование кастомных сообщений

function createError(field, message) {
    return {
        field,
        message
    };
}

Пример

if (!validator.isEmail(email)) {

    errors.push(
        createError(
            'email',
            'Некорректный email'
        )
    );
}

Интеграция с ORM

Sequelize

email: {
    type: DataTypes.STRING,
    validate: {
        isEmail: true
    }
}

TypeORM

@Column()
email;
if (!validator.isEmail(user.email)) {
    throw new Error('Некорректный email');
}

Стратегия многоуровневой валидации

Надёжная система обычно включает несколько уровней:

  1. Проверка в браузере.
  2. Проверка в API.
  3. Проверка бизнес-логики.
  4. Ограничения базы данных.
  5. Санитизация перед сохранением.

Пример ограничения SQL

ALT ER   TABLE users
ADD CONSTRAINT check_age
CHECK (age >= 18);

Практический пример полного цикла

import validator from 'validator';

async function registerUser(data) {

    const errors = {};

    const email =
        validator.normalizeEmail(
            data.email
        );

    const name =
        validator.escape(
            validator.trim(data.name)
        );

    if (!validator.isEmail(email)) {
        errors.email =
            'Некорректный email';
    }

    if (!validator.isLength(name, {
        min: 2,
        max: 50
    })) {
        errors.name =
            'Некорректное имя';
    }

    if (!validator.isLength(
        data.password,
        { min: 8 }
    )) {
        errors.password =
            'Слабый пароль';
    }

    const exists =
        await User.findOne({ email });

    if (exists) {
        errors.email =
            'Email уже используется';
    }

    if (Object.keys(errors).length) {
        throw errors;
    }

    return await User.create({
        email,
        name,
        password: data.password
    });
}