Middleware, Guard, Interceptor, Exception filter
NestJS'da so'rov controller'ga yetib borishidan oldin va javob mijozga qaytishdan oldin bir necha bosqichdan o'tadi. Bu bosqichlar โ Middleware, Guard, Pipe, Interceptor va Exception filter. Har biri o'z vazifasiga ega. Ushbu yakuniy darsda ushbu quvurni (pipeline) va uning har bir bo'g'inini o'rganamiz.
So'rovni qayta ishlash quvuri
Avval umumiy manzarani ko'ramiz. So'rov kelganda u quyidagi tartibda o'tadi. Bu tartibni bilish qaysi vositani qayerda ishlatishni tushunish uchun muhim:
- Middleware โ eng birinchi, xom so'rov ustida (Express middleware kabi);
- Guard โ so'rovni o'tkazish yoki rad etish to'g'risida qaror qabul qiladi (masalan, autentifikatsiya);
- Interceptor (kirish) โ controller'gacha qo'shimcha logika;
- Pipe โ parametrlar va tanani o'zgartirish/tekshirish (masalan, ValidationPipe);
- Controller โ asosiy ish;
- Interceptor (chiqish) โ javobni o'zgartirish;
- Exception filter โ biror joyda xato yuz bersa, uni tutadi.
Middleware
Middleware Express'dagi middleware bilan bir xil g'oyaga ega: u so'rov controller'ga yetishidan oldin ishlaydigan funksiya. Odatda logging, so'rovni tayyorlash yoki umumiy tekshiruvlar uchun ishlatiladi. NestJS'da middleware klass ko'rinishida yoziladi:
// src/logger.middleware.ts
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(req.method + ' ' + req.originalUrl);
next(); // Keyingi bosqichga o'tkazish (Express'dagidek)
}
}
Middleware'ni modulda ro'yxatdan o'tkazamiz โ modul NestModule'ni amalga oshirib, configure metodida uni qaysi marshrutlarga qo'llashni belgilaydi:
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';
@Module({})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*'); // Barcha marshrutlarga qo'llash
}
}
Guard โ autentifikatsiya va avtorizatsiya
Guard bitta savolga javob beradi: "Bu so'rovga ruxsat bermi?". U true qaytarsa, so'rov davom etadi; false qaytarsa yoki istisno tashlasa, NestJS 403 Forbidden qaytaradi. Guard'lar autentifikatsiya (kimligini aniqlash) va avtorizatsiya (huquqini tekshirish) uchun ideal.
Guard CanActivate interfeysini amalga oshiradi. canActivate metodi true/false yoki ularni beruvchi Promise qaytaradi:
// src/auth/auth.guard.ts
import {
Injectable, CanActivate, ExecutionContext, UnauthorizedException,
} from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
// So'rov obyektini olamiz:
const request = context.switchToHttp().getRequest();
const token = request.headers['authorization'];
if (!token) {
throw new UnauthorizedException('Token topilmadi');
}
// Bu yerda token haqiqiyligini tekshirish logikasi bo'ladi
// (masalan, JWT'ni tekshirish)
return true; // Ruxsat berilsa
}
}
Guard'ni marshrut yoki controller'ga @UseGuards dekoratori orqali qo'llaymiz:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../auth/auth.guard';
@Controller('profil')
export class ProfilController {
@Get()
@UseGuards(AuthGuard) // Bu marshrut himoyalangan
getProfil() {
return 'Maxfiy profil ma'lumoti';
}
}
@UseGuards'ni metodga qo'ysangiz โ faqat o'sha metod himoyalanadi. Controller klassiga qo'ysangiz โ undagi barcha metodlar himoyalanadi. Global qilish uchun esa app.useGlobalGuards()'dan foydalaning.Pipe โ o'zgartirish va tekshirish
Pipeni oldingi darsda ko'rgan edik (ValidationPipe, ParseIntPipe). Uning ikki vazifasi bor: o'zgartirish (masalan, satrni songa) va tekshirish (masalan, DTO validatsiyasi). O'zingizning maxsus pipe'ingizni ham yozishingiz mumkin:
// src/pipes/bosh-harf.pipe.ts
import { PipeTransform, Injectable } from '@nestjs/common';
@Injectable()
export class BoshHarfPipe implements PipeTransform {
transform(value: string): string {
// Kelgan satrni bosh harflarga o'giradi:
return value.toUpperCase();
}
}
Bu pipe'ni parametrga qo'llaganda kelgan qiymat controller'ga yetguncha o'zgaradi:
@Get(':nom')
findByName(@Param('nom', BoshHarfPipe) nom: string) {
// Agar /ali so'ralsa, nom = 'ALI'
return nom;
}
Interceptor โ kirish va chiqishni boyitish
Interceptor so'rov oldida ham, javob keyin ham logika qo'shishga imkon beradi. U ayniqsa quyidagilar uchun foydali: barcha javoblarni yagona formatga o'rash, so'rov davomiyligini o'lchash (logging), javobni kesh qilish. Interceptor RxJS'ning Observable'idan foydalanadi:
// src/interceptors/transform.interceptor.ts
import {
Injectable, NestInterceptor, ExecutionContext, CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
// Har bir javobni yagona formatga o'raymiz:
map(data => ({ muvaffaqiyat: true, malumot: data })),
);
}
}
Endi controller [{...}] qaytarsa ham, mijoz shu ko'rinishda javob oladi:
// Controller qaytargan: [{ id: 1, ism: 'Ali' }]
// Mijoz oladigan javob:
// {
// "muvaffaqiyat": true,
// "malumot": [{ "id": 1, "ism": "Ali" }]
// }
Interceptor'ni ham @UseInterceptors dekoratori bilan qo'llaymiz:
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { TransformInterceptor } from '../interceptors/transform.interceptor';
@Controller('users')
@UseInterceptors(TransformInterceptor) // Barcha metodlarga
export class UsersController {
@Get()
findAll() {
return [{ id: 1, ism: 'Ali' }];
}
}
Exception filter โ xatolarni boshqarish
Exception filter ilovada yuz bergan xatolarni tutadi va mijozga qanday ko'rsatishni nazorat qiladi. NestJS'da tayyor istisnolar mavjud: NotFoundException (404), BadRequestException (400), UnauthorizedException (401), ForbiddenException (403). Ularni shunchaki tashlaysiz:
import { NotFoundException, BadRequestException } from '@nestjs/common';
// Service ichida:
if (!user) {
throw new NotFoundException('Foydalanuvchi topilmadi');
}
if (yosh < 18) {
throw new BadRequestException('Yosh 18 dan katta bo'lsin');
}
Xatoni butun ilova bo'ylab yagona formatda ko'rsatish uchun maxsus filter yozamiz. U ExceptionFilter'ni amalga oshiradi va @Catch dekoratori qaysi xatoni tutishni belgilaydi:
// src/filters/http-exception.filter.ts
import {
ExceptionFilter, Catch, ArgumentsHost, HttpException,
} from '@nestjs/common';
import { Response } from 'express';
@Catch(HttpException) // Barcha HTTP istisnolarini tutamiz
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
// Yagona xato formati:
response.status(status).json({
muvaffaqiyat: false,
kod: status,
xabar: exception.message,
vaqt: new Date().toISOString(),
});
}
}
Filter'ni @UseFilters bilan yoki global tarzda qo'llash mumkin:
// Global qo'llash (main.ts):
app.useGlobalFilters(new HttpExceptionFilter());
// Yoki bitta controller/metodga:
// @UseFilters(HttpExceptionFilter)
Barchasini birlashtirish
Amalda bu vositalar birgalikda qo'llaniladi. Quyidagi controller himoyalangan, validatsiyalangan va yagona javob formatiga ega:
import {
Controller, Post, Body, UseGuards, UseInterceptors, UseFilters,
} from '@nestjs/common';
import { AuthGuard } from '../auth/auth.guard';
import { TransformInterceptor } from '../interceptors/transform.interceptor';
import { HttpExceptionFilter } from '../filters/http-exception.filter';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('users')
@UseGuards(AuthGuard) // Autentifikatsiya
@UseInterceptors(TransformInterceptor) // Javob formati
@UseFilters(HttpExceptionFilter) // Xato formati
export class UsersController {
@Post()
create(@Body() dto: CreateUserDto) { // Pipe DTO'ni tekshiradi
return { id: 1, ism: dto.ism };
}
}
Xulosa
- So'rov quvuri tartibi: Middleware → Guard → Interceptor → Pipe → Controller → Interceptor → (xato bo'lsa) Exception filter;
- Middleware โ Express uslubidagi umumiy oldindan qayta ishlash (logging va h.k.);
- Guard (
@UseGuards) โ kirishga ruxsat bor-yo'qligini hal qiladi, autentifikatsiya uchun; - Pipe โ parametr va tanani o'zgartiradi hamda tekshiradi (ValidationPipe, ParseIntPipe);
- Interceptor (
@UseInterceptors) โ kirish/chiqish logikasi, javobni yagona formatga o'rash; - Exception filter (
@Catch,@UseFilters) โ xatolarni tutib, yagona formatda ko'rsatadi; - Bu vositalar mustaqil, qayta ishlatiladigan qatlamlar bo'lib, birgalikda ishonchli API quradi.