Что такое middleware в NestJS?
Middleware в NestJS
Middleware — это промежуточный обработчик, встроенный в цепочку обработки HTTP-запроса между клиентом и конечным обработчиком маршрута. Концепция взята из Express.js, на котором NestJS основан по умолчанию.
Что умеет middleware
- Выполнять произвольный код до обработки запроса
- Изменять объекты
reqиres - Завершить цикл запрос-ответ досрочно
- Вызвать следующий middleware через
next() - Если
next()не вызван — запрос «зависает»
Два способа создания
Класс-based middleware — рекомендованный подход в NestJS:
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// Логируем метод и URL входящего запроса
console.log(`[${req.method}] ${req.url}`);
next(); // Передаём управление дальше
}
}
Функциональный middleware — простой вариант без DI:
import { Request, Response, NextFunction } from 'express';
export function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`[${req.method}] ${req.url}`);
next();
}
Подключение через AppModule
Middleware подключается в модуле через метод configure() с помощью MiddlewareConsumer:
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
// Применяем только к маршрутам /users с методом GET
.forRoutes({ path: 'users', method: RequestMethod.GET });
}
}
Гибкость настройки маршрутов
consumer
.apply(AuthMiddleware, LoggerMiddleware) // Цепочка middleware
.exclude(
{ path: 'auth/login', method: RequestMethod.POST }, // Исключаем публичный эндпоинт
)
.forRoutes('*'); // Применяем ко всем маршрутам
Middleware vs Guards vs Interceptors
| Инструмент | Порядок выполнения | Типичное применение |
|---|---|---|
| Middleware | 1-й | Логирование, базовая трансформация req |
| Guard | 2-й | Аутентификация и авторизация |
| Interceptor | 3-й (до/после) | Трансформация ответа, кэширование |
| Pipe | 4-й | Валидация и трансформация данных |
Важные особенности
Класс-based middleware поддерживает инъекцию зависимостей через @Injectable(), что позволяет использовать сервисы внутри middleware. Функциональный вариант этой возможности лишён.
Global middleware можно подключить через app.use() в main.ts, но такой middleware не имеет доступа к DI-контейнеру NestJS и является чисто Express-уровневым.
Что хочет услышать интервьюер
Понимание места middleware в жизненном цикле запроса NestJS (до Guards, Pipes, Interceptors)
Знание двух способов создания middleware: класс через NestMiddleware и функциональный
Умение подключить middleware через MiddlewareConsumer в модуле с настройкой маршрутов
Понимание разницы между middleware и Guards/Interceptors — когда что применять
Знание, что класс-based middleware поддерживает Dependency Injection, а функциональный — нет
Пример: Класс-based middleware с инъекцией зависимости
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { AuthService } from '../auth/auth.service';
@Injectable()
export class TokenMiddleware implements NestMiddleware {
constructor(private readonly authService: AuthService) {}
async use(req: Request, res: Response, next: NextFunction) {
const token = req.headers['authorization']?.split(' ')[1];
if (token) {
// Декодируем токен и кладём пользователя в объект запроса
req['user'] = await this.authService.decodeToken(token);
}
next();
}
}
Пример: Подключение middleware в модуле
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { TokenMiddleware } from './common/token.middleware';
import { UsersModule } from './users/users.module';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [UsersModule, AuthModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(TokenMiddleware)
.exclude(
// Логин и регистрация доступны без токена
{ path: 'auth/login', method: RequestMethod.POST },
{ path: 'auth/register', method: RequestMethod.POST },
)
.forRoutes('*'); // Применяем ко всем остальным маршрутам
}
}
Типичные ошибки
Путают middleware с Guards — утверждают, что middleware — правильное место для авторизации, хотя Guards лучше подходят для этого
Забывают вызвать next() внутри middleware, из-за чего запрос зависает и не доходит до контроллера
Не знают, что global middleware через app.use() в main.ts не имеет доступа к DI-контейнеру
Не понимают порядок выполнения: думают, что Interceptors выполняются раньше Middleware
Не используют exclude() для исключения публичных маршрутов, пытаясь вместо этого добавлять условия внутри самого middleware


