← Назад к вопросам
Как работает цепочка в NestJS?
2.0 Middle🔥 171 комментариев
#Архитектура и паттерны#Фреймворки и библиотеки
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как работает цепочка в NestJS?
Цепочка (pipeline) в NestJS — это один из ключевых механизмов обработки запросов. Это последовательность операций, через которые проходит каждый запрос перед тем, как попасть в обработчик маршрута. Я активно работал с этим паттерном в production приложениях.
Архитектура обработки запроса
Порядок выполнения в NestJS:
Запрос → Middleware → Guards → Interceptors (before) → Pipes → Controller → Service → Interceptors (after) → Response
Middleware
Самый первый уровень обработки, работает со всеми запросами:
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) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
// Заполнить резервное копирование для логирования
res.on('finish', () => {
console.log(`Response status: ${res.statusCode}`);
});
next();
}
}
// Зарегистрировать в модуле
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
@Module({...})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*'); // Применить ко всем маршрутам
}
}
Guards (Охранники)
Определяют, имеет ли запрос доступ к маршруту:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.split(' ')[1];
if (!token) {
return false;
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
request.user = payload;
return true;
} catch (error) {
return false;
}
}
}
// Использование
@Controller('users')
export class UsersController {
@Get('profile')
@UseGuards(AuthGuard)
getProfile(@Request() req) {
return req.user;
}
}
Interceptors (Перехватчики)
Обрабатывают запрос до и после обработчика:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class TimingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
return next.handle().pipe(
tap(() => {
const duration = Date.now() - start;
console.log(`Request took ${duration}ms`);
})
);
}
}
// Глобальная регистрация
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: TimingInterceptor,
},
],
})
export class AppModule {}
Transformation Interceptor
Нормализация ответов:
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
intercept(context: ExecutionContext, next: CallHandler<T>): Observable<any> {
return next.handle().pipe(
map(data => ({
success: true,
data,
timestamp: new Date().toISOString(),
}))
);
}
}
Pipes (Трубы)
Валидация и трансформация данных:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
// Использование
@Get(':id')
getUser(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findById(id);
}
Validation Pipe с class-validator
import { IsEmail, IsString, MinLength } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(6)
password: string;
}
@Post()
createUser(@Body(new ValidationPipe()) createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
Exception Filter (Обработка ошибок)
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.getResponse(),
timestamp: new Date().toISOString(),
});
}
}
// Глобальная регистрация
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
Полный пример цепочки
@Controller('posts')
@UseInterceptors(TransformInterceptor)
export class PostsController {
constructor(private postsService: PostsService) {}
@Get(':id')
@UseGuards(AuthGuard)
@UseInterceptors(CacheInterceptor)
getPost(
@Param('id', ParseIntPipe) id: number,
@Request() req
) {
console.log('User:', req.user);
return this.postsService.findById(id);
}
@Post()
@UseGuards(AuthGuard)
createPost(
@Body(ValidationPipe) createPostDto: CreatePostDto,
@Request() req
) {
return this.postsService.create(createPostDto, req.user.id);
}
}
Порядок выполнения (важно!)
- Middleware — выполняется первым
- Guards — проверка доступа
- Interceptors (до) — перехват перед обработчиком
- Pipes — валидация параметров
- Controller Handler — основная логика
- Interceptors (после) — трансформация ответа
- Exception Filter — обработка ошибок
Практический пример: Аутентификация + Логирование
@Injectable()
export class RequestLoggerInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const { method, url, user } = context.switchToHttp().getRequest();
const start = Date.now();
return next.handle().pipe(
tap(() => {
const duration = Date.now() - start;
console.log(`${user?.id} ${method} ${url} ${duration}ms`);
}),
catchError(error => {
console.error(`Error in ${method} ${url}:`, error.message);
throw error;
})
);
}
}
Best Practices
- Используй Guards для аутентификации — это их основное предназначение
- Interceptors для кросс-кар проблем — логирование, кеширование, трансформация
- Pipes для валидации — используй class-validator для структурированности
- Exception Filters для единообразной обработки ошибок
- Регистрируй глобально через APP_ tokens* для применения ко всему приложению
- Помни про порядок выполнения — это критично для правильной работы
Полное понимание цепочки обработки позволяет создавать чистый, масштабируемый код с правильным разделением ответственности.