====== JOI - валідація ======
[[https://github.com/hapijs/joi/|joi - це бібліотека для валідації даних в JavaScript-середовищі, зокрема в Node.js.]
===== Налаштування =====
1. **Визначення схем (Schema definition)**: ви можете описати повністю схеми для об'єктів, які бажаєте валідувати, використовуючи методи Joi.object() та Joi.array() для структур та методи для примітивів (числа, рядки, бульові значення тощо). Схеми є семантично зрозумілими, і ви легко зможете зрозуміти, що робить кожне з правил. Переглянути список усіх доступних правил можна в офіційній документації. Створимо схему валідації для об’єкта при створенні нового студента:
// src/validation/students.js
import Joi from 'joi';
export const createStudentSchema = Joi.object({
name: Joi.string().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(6).max(16).required(),
gender: Joi.string().valid('male', 'female', 'other').required(),
avgMark: Joi.number().min(2).max(12).required(),
onDuty: Joi.boolean(),
});
===== Використання =====
2. **Використання методів валідації:** після визначення схеми ви можете використовувати на ній методи валідації, такі як validate або validateAsync, для перевірки об'єктів даних на відповідність цій схемі.
const dataToValidate = {
name: 'John Doe',
email: 'john.doe@example.com',
age: 12,
gender: 'male',
avgMark: 10.2,
};
const validationResult = createStudentSchema.validate(dataToValidate);
if (validationResult.error) {
console.error(validationResult.error.message);
} else {
console.log('Data is valid!');
}
3. **Кастомізація помилок при роботі з Joi** - У Joi ви можете кастомізувати повідомлення про помилки для кожного правила валідації та для конкретних умов. Це дозволяє вам надати більш інформативні повідомлення для ваших користувачів або розробників, які обробляють ці помилки.
import Joi from 'joi';
// Оголошення схеми з кастомізованими повідомленнями
const createStudentSchema = Joi.object({
name: Joi.string().min(3).max(30).required().messages({
'string.base': 'Username should be a string', // Кастомізація повідомлення для типу "string"
'string.min': 'Username should have at least {#limit} characters',
'string.max': 'Username should have at most {#limit} characters',
'any.required': 'Username is required',
}),
email: Joi.string().email().required(),
age: Joi.number().integer().min(6).max(16).required(),
gender: Joi.string().valid('male', 'female', 'other').required(),
avgMark: Joi.number().min(2).max(12).required(),
onDuty: Joi.boolean(),
});
**P.S.** Можна вказати { abortEarly: false } при виклику методу validate, щоб отримати всі можливі помилки валідації, а не першу з них:
const validationResult = createStudentSchema.validate(userData, {
abortEarly: false,
});
===== Middleware валідації =====
==== Валідація у контроллері ====
Ми можемо напряму викликати валідацію **req.body** в тілі контролера:
//...
app.post(
'/students',
async (req, res, next) => {
try {
await createStudentSchema.validateAsync(req.body, { abortEarly: false });
//..
} catch (validationError) {
next(validationError);
}
},
);
Зверни увагу, що ми використовуємо**validateAsync** , тому що це дозволить нам виконувати асинхронні операції під час валідації, забезпечуючи можливість розширення функціоналу. Це зручно для майбутніх сценаріїв, де можуть бути додані асинхронні перевірки, або коли валідація може вимагати звернень до зовнішніх ресурсів.
Але таке рішення буде перенавантажувати логіку в тілі контроллера, тому непоганим рішенням буде винести валідацію в окремий middleware validateBody. Створи для нього окремий файл у папці middlewares:
// src/middlewares/validateBody.js
import createHttpError from 'http-errors';
export const validateBody = (schema) => async (req, res, next) => {
try {
await schema.validateAsync(req.body, {
abortEarly: false,
});
next();
} catch (err) {
const error = createHttpError(400, 'Bad Request', {
errors: err.details,
});
next(error);
}
};