В экосистеме серверных приложений на Node.js middleware выступает промежуточным слоем между входящим HTTP-запросом и бизнес-логикой. Основная задача — обеспечить предсказуемость входных данных до того, как они попадут в обработчики маршрутов.
Валидация через middleware позволяет отделить проверку структуры данных от основной логики приложения, снижая связность компонентов и повышая тестируемость.
Celebrate представляет собой специализированный middleware-слой, построенный на базе схем валидации Joi и предназначенный для интеграции с Express.
Celebrate реализует принцип декларативной валидации: структура запроса описывается схемами, которые применяются к конкретным частям HTTP-запроса.
Обрабатываемые области запроса:
body — тело запросаquery — параметры строки запросаparams — маршрутные параметрыheaders — HTTP-заголовкиcookies — cookies (при соответствующей настройке)Каждая область может иметь собственную схему валидации, что позволяет изолировать правила проверки.
Типовой сценарий подключения middleware:
const express = require('express');
const { celebrate, Joi, Segments } = require('celebrate');
const app = express();
app.use(express.json());
app.post(
'/users',
celebrate({
[Segments.BODY]: Joi.object().keys({
username: Joi.string().alphanum().min(3).max(30).required(),
email: Joi.string().email().required(),
age: Joi.number().integer().min(0)
})
}),
(req, res) => {
res.json({ status: 'ok' });
}
);
В данном примере:
bodySegments определяет область применения схемы:
Segments.BODYSegments.QUERYSegments.PARAMSSegments.HEADERSПример комбинированной валидации:
celebrate({
[Segments.PARAMS]: Joi.object({
id: Joi.string().uuid().required()
}),
[Segments.QUERY]: Joi.object({
verbose: Joi.boolean()
})
})
Разделение позволяет локализовать ошибки и упрощает поддержку маршрутов.
Схемы основаны на композиции валидаторов:
Joi.string().trim().min(5).max(50)
Joi.number().integer().positive()
Joi.object({
title: Joi.string().required(),
content: Joi.string().required(),
tags: Joi.array().items(Joi.string())
})
Joi.object({
user: Joi.object({
id: Joi.number().required(),
profile: Joi.object({
nickname: Joi.string()
})
})
})
Celebrate генерирует ошибки при несоответствии схемам. Для централизованной обработки используется middleware обработки ошибок.
const { errors } = require('celebrate');
app.use(errors());
Формат ошибки содержит:
body, query,
params)Пример структуры ответа:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Validation failed",
"validation": {
"body": {
"source": "body",
"keys": ["email"],
"message": "\"email\" must be a valid email"
}
}
}
Joi поддерживает переопределение сообщений:
Joi.string().email().messages({
'string.email': 'Некорректный формат email'
})
Celebrate сохраняет эти сообщения в итоговом ответе, что позволяет унифицировать пользовательский интерфейс ошибок.
Валидация HTTP-заголовков используется для защиты API от некорректных или вредоносных запросов:
celebrate({
[Segments.HEADERS]: Joi.object({
authorization: Joi.string().required()
}).unknown()
})
Метод .unknown() необходим для разрешения стандартных
заголовков, не указанных в схеме.
В крупных приложениях Celebrate применяется на уровне маршрутизаторов:
const router = require('express').Router();
router.get(
'/posts/:id',
celebrate({
[Segments.PARAMS]: Joi.object({
id: Joi.number().required()
})
}),
handler
);
Такой подход позволяет локализовать правила валидации рядом с маршрутами.
Для масштабируемости схемы выносятся в отдельные модули:
const userSchema = Joi.object({
username: Joi.string().required(),
password: Joi.string().min(8)
});
module.exports = { userSchema };
Использование:
celebrate({
[Segments.BODY]: userSchema
})
Схемы можно объединять:
const baseSchema = Joi.object({
id: Joi.number().required()
});
const extendedSchema = baseSchema.keys({
name: Joi.string().required()
});
Такой подход снижает дублирование и упрощает сопровождение.
Celebrate выполняет проверку синхронно в контексте middleware цепочки Express. Это обеспечивает предсказуемую остановку запроса при первой ошибке валидации без обращения к бизнес-логике.
По умолчанию Joi может собирать несколько ошибок или останавливаться на первой. Поведение регулируется опцией:
Joi.object().options({ abortEarly: false })
При отключении раннего прерывания формируется полный список нарушений схемы.
Использование Celebrate снижает риск:
Валидация на уровне middleware выполняет роль первого фильтра входных данных.
app.use(errors());
app.use((err, req, res, next) => {
res.status(500).json({ message: 'Internal error' });
});
Celebrate не решает задачи:
Он ограничивается структурной проверкой входных данных.
Joi позволяет определять пользовательские правила:
Joi.string().custom((value, helpers) => {
if (!value.startsWith('APP_')) {
return helpers.error('string.prefix');
}
return value;
})
Это расширяет возможности схем без изменения архитектуры middleware.