Node.jsМодуль 5: Базы данных
Валидация данных
Joi, Zod, express-validator
Цель урока
В этом уроке ты научишься:
- Валидировать данные с Joi
- Использовать Zod для TypeScript
- Применять express-validator
Joi
Установка
npm install joiБазовое использование
const Joi = require('joi');
const userSchema = Joi.object({
name: Joi.string().min(2).max(50).required(),
email: Joi.string().email().required(),
password: Joi.string().min(6).required(),
age: Joi.number().integer().min(18).max(120),
role: Joi.string().valid('user', 'admin').default('user')
});
// Валидация
const { error, value } = userSchema.validate(req.body);
if (error) {
return res.status(400).json({ error: error.details[0].message });
}Middleware
function validate(schema) {
return (req, res, next) => {
const { error, value } = schema.validate(req.body, {
abortEarly: false, // Все ошибки
stripUnknown: true // Удалить неизвестные поля
});
if (error) {
const errors = error.details.map(d => ({
field: d.path.join('.'),
message: d.message
}));
return res.status(400).json({ errors });
}
req.body = value;
next();
};
}
// Использование
app.post('/users', validate(userSchema), (req, res) => {
// req.body уже валидирован
});Сложные схемы
const postSchema = Joi.object({
title: Joi.string().min(5).max(200).required(),
content: Joi.string().min(10).required(),
tags: Joi.array().items(Joi.string()).min(1).max(5),
publishAt: Joi.date().greater('now'),
author: Joi.object({
id: Joi.number().required(),
name: Joi.string()
}).required()
});
// Условная валидация
const schema = Joi.object({
type: Joi.string().valid('personal', 'business').required(),
companyName: Joi.when('type', {
is: 'business',
then: Joi.string().required(),
otherwise: Joi.forbidden()
})
});Zod (TypeScript)
Установка
npm install zodБазовое использование
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
password: z.string().min(6),
age: z.number().int().min(18).max(120).optional(),
role: z.enum(['user', 'admin']).default('user')
});
// Тип выводится автоматически
type User = z.infer<typeof userSchema>;
// Валидация
const result = userSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.errors });
}
const user: User = result.data;Middleware
import { z, ZodSchema } from 'zod';
import { Request, Response, NextFunction } from 'express';
function validate<T>(schema: ZodSchema<T>) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
errors: result.error.errors.map(e => ({
field: e.path.join('.'),
message: e.message
}))
});
}
req.body = result.data;
next();
};
}Продвинутые возможности
// Трансформация
const schema = z.string().transform(val => val.toLowerCase());
// Уточнение
const passwordSchema = z.string()
.min(8)
.refine(val => /[A-Z]/.test(val), 'Нужна заглавная буква')
.refine(val => /[0-9]/.test(val), 'Нужна цифра');
// Объединение схем
const baseSchema = z.object({ id: z.number() });
const extendedSchema = baseSchema.extend({ name: z.string() });
// Частичная схема
const partialSchema = userSchema.partial(); // Все поля optionalexpress-validator
Установка
npm install express-validatorИспользование
const { body, param, query, validationResult } = require('express-validator');
app.post('/users',
body('email').isEmail().normalizeEmail(),
body('password').isLength({ min: 6 }).trim(),
body('name').notEmpty().escape(),
body('age').optional().isInt({ min: 18 }),
(req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Данные валидны
res.json(req.body);
}
);Кастомные валидаторы
const { body } = require('express-validator');
body('email').custom(async (email) => {
const user = await User.findOne({ email });
if (user) {
throw new Error('Email уже используется');
}
});
body('confirmPassword').custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error('Пароли не совпадают');
}
return true;
});Сравнение
| Критерий | Joi | Zod | express-validator |
|---|---|---|---|
| TypeScript | Частично | Отлично | Частично |
| Размер | Большой | Средний | Маленький |
| Синтаксис | Fluent | Fluent | Middleware |
| Трансформация | Да | Да | Да |
Практика
Задание: Схема регистрации
Задача: Создай схему для регистрации с подтверждением пароля.
Решение (Joi):
const registerSchema = Joi.object({
email: Joi.string().email().required(),
password: Joi.string().min(8).required(),
confirmPassword: Joi.string()
.valid(Joi.ref('password'))
.required()
.messages({ 'any.only': 'Пароли не совпадают' }),
name: Joi.string().min(2).max(50).required()
});Проверь себя
- Как валидировать email в Joi?
- Что делает
safeParseв Zod? - Как создать кастомный валидатор?