Middleware скрывает логику обработки запроса за цепочкой функций, что усложняет:
Типичный middleware часто делает слишком много:
app.use((req, res, next) => {
// Аутентификация
// Логирование
// Валидация
// Преобразование данных
next();
});
Middleware сложно тестировать изолированно из-за:
next()
req/res
Middleware часто полагается на:
req
Обработка ошибок в middleware становится запутанной:
app.use((err, req, res, next) => {
// Какие middleware вызвали ошибку?
// Какой контекст был при ошибке?
});
Вместо:
app.use(authMiddleware);
app.use(loggingMiddleware);
app.get('/route', handler);
Используйте:
const withAuth = (handler) => async (req, res) => {
const user = await authenticate(req);
return handler({ ...req, user }, res);
};
const withLogging = (handler) => async (req, res) => {
logRequest(req);
return handler(req, res);
};
app.get('/route', withLogging(withAuth(handler)));
function authenticated(handler) {
return async (req, res) => {
if (!req.headers.authorization) {
return res.status(401).send();
}
return handler(req, res);
};
}
app.get('/protected', authenticated(async (req, res) => {
// Обработчик маршрута
}));
class OrderHandler {
constructor(dependencies) {
this.authService = dependencies.authService;
this.logger = dependencies.logger;
}
async handle(req, res) {
try {
this.logger.log(req);
const user = await this.authService.verify(req);
// Основная логика
} catch (err) {
this.handleError(err, res);
}
}
}
const processRequest = pipe(
validateInput,
authenticate,
authorize,
handleRequest,
formatResponse
);
app.post('/api', async (req, res) => {
const result = await processRequest(req);
res.json(result);
});
// Настройка DI
const container = new Container();
container.register('authService', AuthService);
container.register('orderController', OrderController);
// Маршрут с явными зависимостями
app.get('/orders', (req, res) => {
const controller = container.resolve('orderController');
return controller.handle(req, res);
});
test('auth wrapper', async () => {
const mockHandler = jest.fn();
const wrapped = authenticated(mockHandler);
await wrapped(mockReq, mockRes);
expect(mockHandler).toHaveBeenCalled();
});
class UserController {
constructor({ userService, authService }) {
this.userService = userService;
this.authService = authService;
}
}
// Было:
app.use((req, res, next) => {
req.user = parseUser(req);
next();
});
// Стало:
const enrichRequestWithUser = (req) => ({
...req,
user: parseUser(req)
});
// Было:
app.use(auth);
app.use(logging);
app.get('/path', handler);
// Стало:
app.get('/path', pipe(
enrichWithUser,
logRequest,
handler
));
// Было:
app.use((err, req, res, next) => {...});
// Стало:
app.get('/path', async (req, res) => {
try {
// ...
} catch (err) {
errorHandler(err, res);
}
});
отказ от middleware в пользу явных композиций, декораторов и DI делает приложение более предсказуемым, тестируемым и поддерживаемым. Хотя middleware удобен для быстрого старта, в долгосрочной перспективе явные подходы окупаются за счет лучшей архитектуры и понятности кода.