Промежуточные обработчики (middleware)
Промежуточный обработчик (middleware) — функция, которая получает запрос раньше контроллера. Это те же middleware, что в Express: у них есть объекты req, res и функция next(), передающая запрос дальше. Их используют для общих задач — записи запросов в журнал, установки заголовков, подсчёта времени.
Создание
nest g middleware common/middleware/request-log --flat// src/common/middleware/request-log.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
@Injectable()
export class RequestLogMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.originalUrl}`);
next();
}
}Класс реализует интерфейс NestMiddleware с методом use(). Вызов next() обязателен: без него запрос так и останется в обработчике.
Подключение
У middleware нет места в декораторе @Module(). Его подключают в методе configure() модуля:
// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RequestLogMiddleware } from './common/middleware/request-log.middleware';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [PostsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestLogMiddleware).forRoutes('posts');
}
}apply() называет обработчик, forRoutes() — адреса, для которых он работает. Откройте /posts и /posts/1 — в терминале сервера появятся строки GET /posts и GET /posts/1.
Выбор маршрутов
// src/app.module.ts
import { MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { PostsController } from './posts/posts.controller';
configure(consumer: MiddlewareConsumer) {
consumer
.apply(RequestLogMiddleware)
.exclude({ path: 'posts/popular', method: RequestMethod.GET })
.forRoutes(PostsController);
}В forRoutes() можно передать контроллер — тогда обработчик сработает для всех его маршрутов. exclude() убирает отдельные адреса. Несколько обработчиков перечисляют через запятую: apply(First, Second).
Функция вместо класса
Если обработчику не нужны сервисы, достаточно обычной функции:
// src/common/middleware/powered-by.middleware.ts
import { NextFunction, Request, Response } from 'express';
export function poweredBy(req: Request, res: Response, next: NextFunction) {
res.setHeader('X-Powered-By', 'Blog API');
next();
}Её подключают так же: consumer.apply(poweredBy).forRoutes('posts'). Для всего приложения готовые Express-обработчики можно включить в src/main.ts через app.use().
Middleware или другие инструменты
Middleware ничего не знает о том, какой контроллер и метод будут вызваны. Поэтому проверку доступа удобнее делать гардами, а изменение ответа — перехватчиками: оба видят, какой именно метод обрабатывает запрос. Эти инструменты описаны в следующих уроках.
Частая ошибка
Забытый вызов next() — запрос «повисает»: клиент ждёт ответа, пока не сработает тайм-аут, а в журнале нет ни одной ошибки. Если после подключения middleware сервер перестал отвечать, первым делом проверьте, что next() вызывается во всех ветках кода.