Использование в Node.js опирается на идею единых схем валидации, которые применяются как на клиенте, так и на сервере. Это снижает расхождения между фронтенд- и бэкенд-логикой, особенно в проектах, где требуется строгая синхронизация правил проверки данных.
Библиотека Vest реализует декларативный подход к описанию проверок, где набор правил формируется как последовательность тестов, группируемых по логическим блокам. В Node.js она применяется как часть слоя валидации входящих данных, middleware в веб-фреймворках, а также как отдельный модуль доменной логики.
В среде Node.js библиотека устанавливается как обычная зависимость npm-пакета и может использоваться как в CommonJS, так и в ESM-окружении.
Основные варианты подключения:
// ESM
import { test, enforce, group, skip } from 'vest';
// CommonJS
const { test, enforce, group, skip } = require('vest');
Node.js-проекты часто разделяют валидацию на отдельные модули:
/src
/validation
user.validation.js
auth.validation.js
product.validation.js
Такой подход позволяет изолировать правила и переиспользовать их в контроллерах, сервисах и тестах.
В основе работы лежит функция, возвращающая результат набора тестов. В Node.js это удобно интегрируется в сервисный слой.
import { create, test, enforce } from 'vest';
const validateUser = create((data = {}) => {
test('username', 'Имя пользователя обязательно', () => {
enforce(data.username).isNotEmpty();
});
test('email', 'Некорректный email', () => {
enforce(data.email).matches(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
});
export default validateUser;
На сервере результат используется как объект, содержащий информацию о валидности и деталях ошибок.
Node.js чаще всего применяет валидацию на уровне middleware. В связке с Express логика проверки отделяется от бизнес-кода.
import express from 'express';
import validateUser from './validation/user.validation.js';
const app = express();
app.use(express.json());
app.post('/users', (req, res) => {
const result = validateUser(req.body);
if (result.hasErrors()) {
return res.status(400).json({
errors: result.getErrors(),
});
}
res.status(201).json({ ok: true });
});
Такой подход позволяет централизовать правила и не дублировать проверки в контроллерах.
Одной из особенностей Vest является возможность повторного выполнения только изменённых частей логики. В Node.js это критично при высокой нагрузке.
Пример использования кэширования через идентификатор данных:
import { create, test, enforce } from 'vest';
const validateProduct = create((data = {}, changedFields = []) => {
test.memo('name', () => changedFields.includes('name'), () => {
enforce(data.name).longerThan(2);
});
test.memo('price', () => changedFields.includes('price'), () => {
enforce(data.price).greaterThan(0);
});
});
В серверной архитектуре это снижает стоимость повторной валидации при частичных обновлениях ресурсов (PATCH-запросы).
Node.js часто работает с внешними источниками: базами данных, API, очередями. Валидация может включать асинхронные проверки.
import { create, test, enforce } from 'vest';
const validateRegister = create(async (data = {}) => {
test('email_unique', 'Email уже используется', async () => {
const exists = await checkEmailInDatabase(data.email);
enforce(exists).equals(false);
});
});
Асинхронные тесты выполняются последовательно внутри группы, сохраняя предсказуемость результата.
Группы позволяют логически разделять правила валидации. В Node.js это особенно полезно для сложных DTO.
import { create, group, test, enforce } from 'vest';
const validateOrder = create((data = {}) => {
group('customer', () => {
test('name', () => {
enforce(data.customer?.name).isNotEmpty();
});
test('phone', () => {
enforce(data.customer?.phone).matches(/^\+?[0-9]{10,15}$/);
});
});
group('delivery', () => {
test('address', () => {
enforce(data.delivery?.address).isNotEmpty();
});
});
});
Такое разделение удобно при логировании и трассировке ошибок на сервере.
В Node.js-приложениях валидация часто размещается между контроллером и сервисом.
Типичная схема:
Request → Controller → Validation Layer → Service → Repository
Валидация с Vest выполняет роль фильтра входных данных до попадания в бизнес-логику.
async function createUserHandler(req, res) {
const result = validateUser(req.body);
if (result.hasErrors()) {
return res.status(400).json(result.getErrors());
}
const user = await userService.create(req.body);
res.json(user);
}
В TypeScript-среде библиотека используется без потери типизации входных данных.
import { create, test, enforce } from 'vest';
type User = {
email: string;
username: string;
};
const validateUser = create((data: Partial<User>) => {
test('email', () => {
enforce(data.email).isString();
});
test('username', () => {
enforce(data.username).isString();
});
});
Типизация помогает переносить правила валидации ближе к контракту данных, уменьшая количество runtime-ошибок.
Node.js-функции в serverless (AWS Lambda, Cloud Functions) требуют минимального времени старта. Валидация должна быть лёгкой и повторно используемой.
export const handler = async (event) => {
const body = JSON.parse(event.body);
const result = validateUser(body);
if (result.hasErrors()) {
return {
statusCode: 400,
body: JSON.stringify(result.getErrors()),
};
}
return {
statusCode: 200,
body: JSON.stringify({ ok: true }),
};
};
В Node.js часто требуется преобразование результата в формат API-ответа.
function formatErrors(result) {
return result.getErrors().map(err => ({
field: err.fieldName,
message: err.message,
}));
}
Структура ошибок может быть адаптирована под JSON API, GraphQL или собственные протоколы.
На сервере важно фиксировать причины отказа валидации. Vest позволяет извлекать детализированные данные о каждом тесте.
if (result.hasErrors()) {
console.error('Validation failed', result.getErrors());
}
В распределённых системах такие логи используются для анализа качества входящих данных и мониторинга API.
Node.js работает в однопоточном режиме, поэтому стоимость валидации напрямую влияет на пропускную способность сервера. Оптимизация достигается за счёт:
Vest спроектирована так, чтобы минимизировать повторные вычисления при неизменных данных, что особенно важно при высокой частоте запросов.
Одним из ключевых сценариев является общий пакет валидации:
/packages
/validation
user.rules.js
Этот пакет импортируется и в фронтенд, и в Node.js:
import validateUser from '@shared/validation/user.rules';
Такой подход устраняет расхождения между клиентской и серверной логикой, снижая количество ошибок интеграции.