8.6-bob: Guards, Interceptors, Middleware, Exception Filters (chuqur)
8-QISM — NestJS (chuqur) · 6-mavzu
1. Kirish va motivatsiya
DTO/validatsiyani 8.5-bob bildik. Endi NestJS'ning eng kuchli, "sehrli" mexanizmlariga — request lifecycle (8.1: 2.12) qatlamlariga — o'tamiz: Middleware, Guards, Interceptors, Exception Filters (+ Pipes — 8.5). Bular — cross-cutting concerns (ko'ndalang masalalar): auth, logging, kesh, xato — har endpoint'da takrorlanadigan ishlarni bir joyda, deklarativ hal qiladi. Bu — NestJS'ni Express'dan keskin ajratadigan, professional kodning asosi.
Express'da auth, log, xato — har joyda qo'lda middleware (5.6, 5.10). NestJS — bularni maxsus, tashkillangan qatlamlarga ajratadi: Guard (kirishga ruxsatmi — auth/RBAC — 5.15, 5.17), Interceptor (so'rov/javobni o'rab olish — log, transform, kesh, timeout), Middleware (eng past daraja — so'rov oldidan), Exception Filter (xatoni ushlash — 5.10). Har biri request lifecycleda aniq o'rinda ishlaydi (8.1: 2.12). Bu — toza, qayta ishlatiladigan, deklarativ kod.
Eng muhimi: request lifecycle tartibini tushunish (8.1: 2.12 — bu yerda chuqur). So'rov: Middleware Guard Interceptor (oldin) Pipe Handler Interceptor (keyin) Filter (xato bo'lsa). Qaysi vazifa qaysi qatlamga tegishli — bu bilim NestJS'ni "bilaman" deyishning belgisi. Bu bob: to'rt mexanizm (guard, interceptor, middleware, filter — pipe 8.5'da), RxJS interceptor'da, ExecutionContext, custom decorator, va tartib — chuqur. Bu — auth 8.9-bob, RBAC 8.7-bob, log, xato — hammasining asosi.
O'xshatish (request lifecycle): so'rov — aeroportdagi yo'lovchi. Middleware — kirish eshigi (umumiy tekshiruv — log). Guard — pasport nazorati (kirishga ruxsatmi — auth — 5.15). Pipe — bagaj tekshiruvi (ma'lumot to'g'rimi — validatsiya — 8.5). Handler — samolyot (asosiy ish). Interceptor — xizmat (uchishdan oldin/keyin — ovqat, e'lon — log/transform). Exception Filter — favqulodda xizmat (muammo bo'lsa — xato boshqaruvi). Har biri aniq o'rinda, aniq vazifa.
Nega muhim?
- Cross-cutting — auth/log/xato bir joyda (takrorlamasdan).
- Request lifecycle — NestJS'ni tushunishning kaliti.
- Auth/RBAC asosi — guard (8.7, 8.9).
- Professional kod — deklarativ, toza, qayta ishlatiladigan.
2. Nazariya — chuqur tushuntirish
2.1. Request lifecycle (to'liq tartib — 8.1: 2.12 chuqur)
HTTP so'rov
1. MIDDLEWARE (umumiy — log, header)
2. GUARDS (ruxsatmi — auth/RBAC — 5.15, 5.17)
3. INTERCEPTORS (oldin) (so'rovni o'rab — log boshlash, kesh tekshir)
4. PIPES (validatsiya/transform — 8.5)
5. CONTROLLER HANDLER (route metod service)
6. INTERCEPTORS (keyin) (javobni o'rab — transform, log tugatish)
7. EXCEPTION FILTERS (xato bo'lsa — istalgan bosqichda)
HTTP javobRequest lifecycle tartibi — eng muhim tushuncha: har qatlam aniq o'rinda. Guard (3) pipe (4) dan oldin auth validatsiyadan oldin (ruxsatsiz — validatsiyaga ham bormaydi). Interceptor handler'ni o'raydi (oldin + keyin). Filter — istalgan joyda xato. Bu tartib — qaysi vazifa qayerda.
2.2. Qaysi mexanizm qaysi vazifa (tanlov)
┌─────────────┬──────────────────────────────────────┐
│ Mexanizm │ Vazifa │
├─────────────┼──────────────────────────────────────┤
│ Middleware │ umumiy (log, header, body parse) │
│ Guard │ RUXSAT (auth, RBAC — true/false) │
│ Interceptor │ o'rash (transform, log, kesh, timeout)│
│ Pipe 8.5-bob │ validatsiya/transform (ma'lumot) │
│ Exception Filter│ XATO (ushlash, javob) │
└─────────────┴──────────────────────────────────────┘Tanlov: Guard — "kirsa bo'ladimi?" (ruxsat — true/false). Interceptor — "so'rov/javobni o'zgartir/kuzat" (log, transform, kesh). Middleware — eng umumiy (past daraja). Pipe — ma'lumot 8.5-bob. Filter — xato. Har vazifaga to'g'ri mexanizm (auth guard, log interceptor).
2.3. Middleware (eng past daraja)
Middleware — so'rov controller'ga yetmasdan oldin ishlaydi (Express middleware — 5.6 — docs):
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.url}`); // log (5.12)
next(); // keyingi (5.6)
}
}
// Modulda ulash
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes("*"); // barcha route
}
}Middleware — Express middleware 5.6-bob ning NestJS versiyasi (
NestMiddleware,use). Eng past daraja (req/res to'g'ridan). Umumiy ish (log, header, raw body).configureda ulanadi (modulda). Lekin middleware ExecutionContext'ni bilmaydi (qaysi controller/handler — guard/interceptor biladi). Auth guard afzal 2.4-bob.
Middleware ikki ko'rinishda yoziladi — class (yuqorida, NestMiddleware) va funksional (oddiy funksiya — holatsiz, DI shart bo'lmaganda):
// Funksional middleware (holatsiz — DI kerak bo'lmasa — eng yengil)
import { Request, Response, NextFunction } from "express";
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.url}`);
next(); // keyingi 5.6-bob — chaqirilmasa so'rov osiladi
}Class vs funksional: class middleware —
@Injectable(), DI ishlaydi (service inject qilish mumkin — masalan config, log service). Funksional middleware — oddiy funksiya, DI yo'q, lekin yengil (holatsiz ish uchun). Docs tavsiyasi: DI kerak bo'lmasa — funksional (soddaroq).
MiddlewareConsumer — moslashuvchan ulash. configure metodida middleware'ni qaysi route'larga ulashni aniq boshqarasiz:
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from "@nestjs/common";
@Module({ /* ... */ })
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware, logger) // bir nechta middleware (tartib bilan)
.exclude( // istisno route'lar (middleware o'tkazib yuboradi)
{ path: "auth/login", method: RequestMethod.POST },
"health", // path string ham mumkin
)
.forRoutes(
{ path: "users", method: RequestMethod.GET }, // faqat GET /users
UsersController, // yoki butun controller
);
}
}
apply().forRoutes()— asosiy shakl.forRoutesqabul qiladi: string ("users"),{ path, method }obyekti (aniq metod), yoki controller class (uning barcha route'lari)."*"— hamma route (wildcard).exclude— istisno route'lar (masalan/health,/login— auth log kerak emas).applyga bir nechta middleware berilsa — ketma-ket (berilgan tartibda) ishlaydi.
Global middleware — modul configure'siz, main.ts'da to'g'ridan (Express app.use — hamma route):
// main.ts — global middleware (barcha route, MiddlewareConsumer'siz)
import { logger } from "./logger.middleware";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.use(logger); // funksional middleware — global
await app.listen(3000);
}
app.use()— faqat funksional middleware qabul qiladi (class emas — DI konteynerdan o'tmaydi). DI kerak global middleware uchun — moduldaforRoutes("*")ishlating.app.use— Express bilan bir xil (raw body, third-party middleware — helmet, cookie-parser — 5.6).
2.4. Guard (ruxsat — auth/RBAC)
Guard — so'rovga ruxsat beradi yoki rad etadi (CanActivate — docs):
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean { // true — ruxsat; false — 403
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization;
if (!token) return false; // ruxsat yo'q 403
// ... token tekshir 5.15-bob ...
return true; // ruxsat
}
}
// Ishlatish (dekorator)
@UseGuards(AuthGuard)
@Get("profile")
profile() {}Guard —
CanActivateinterface (canActivate—boolean/Promise<boolean>).trueruxsat (handler ishlaydi);false403 (avtomatik). ExecutionContext 2.7-bob — qaysi handler ishlashini biladi (metama'lumot — RBAC). Auth 8.9-bob, RBAC 8.7-bob — guard bilan. Pipe'dan oldin (ruxsatsiz — validatsiyaga ham bormaydi — 2.1).
2.5. Interceptor (o'rash — transform/log/kesh)
Interceptor — handler'ni o'rab oladi (oldin + keyin — RxJS — docs):
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
import { tap, map } from "rxjs/operators";
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const boshlandi = Date.now();
console.log("So'rov boshlandi"); // OLDIN (handler'dan oldin)
return next.handle().pipe( // handler'ni chaqir
tap(() => console.log(`Vaqt: ${Date.now() - boshlandi}ms`)), // KEYIN
);
}
}Interceptor —
NestInterceptor(intercept— RxJS Observable).next.handle()— handler'ni chaqiradi; uning oldin (kodda before) va keyin (.pipe— RxJS) ishlash mumkin. Transform (javobni o'zgartirish), log (timing), kesh, timeout. RxJS 2.6-bob — interceptor'ning asosi.
2.6. RxJS interceptor'da (Observable, map/tap)
// Javobni o'rash (standart format — 5.7)
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => ({ success: true, data })), // javobni o'rab (5.7)
);
}
}
// Handler { id: 1 } qaytarsa { success: true, data: { id: 1 } }RxJS (interceptor'da):
next.handle()—Observable(handler natijasi).map— javobni o'zgartirish (standart format — 5.7);tap— yon ta'sir (log — javobni o'zgartirmasdan);catchError— xato;timeout— vaqt cheklash. NestJS RxJS ishlatadi (FRP — 8.1). Asosiy operatorlar yetadi (chuqur RxJS shart emas).
2.7. ExecutionContext (qaysi handler — metadata)
ExecutionContext — qaysi controller/handler ishlashini va metama'lumotni biladi:
import { Reflector } from "@nestjs/core";
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {} // DI (8.2)
canActivate(context: ExecutionContext): boolean {
// Metama'lumotni o'qish (@Roles dekoratoridan — 2.9)
const rollar = this.reflector.get<string[]>("roles", context.getHandler());
if (!rollar) return true; // rol talab qilinmagan
const { user } = context.switchToHttp().getRequest();
return rollar.includes(user.rol); // RBAC (5.17)
}
}ExecutionContext — guard/interceptor'ga kontekst beradi:
switchToHttp().getRequest()(req),getHandler()(qaysi metod),getClass()(controller). Reflector (DI) —@SetMetadata/custom dekorator 2.9-bob metama'lumotini o'qiydi (RBAC rollari). Bu — guard'ni dinamik qiladi (handler'ga qarab).
2.8. Exception Filter (xato ushlash — 5.10)
Exception Filter — xatoni ushlab, javob qaytaradi (5.10 — docs):
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from "@nestjs/common";
@Catch() // barcha xato (yoki @Catch(HttpException))
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception instanceof HttpException
? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;
response.status(status).json({ // izchil xato javob (5.7, 5.10)
success: false,
statusCode: status,
message: exception instanceof HttpException ? exception.message : "Server xatosi",
});
}
}Exception Filter —
ExceptionFilter(catch),@Catch()(qaysi xato). NestJS built-in filter bor (HttpException avtomatik JSON — 8.1: 2.6). Custom filter — izchil format 5.7-bob, log 5.12-bob, xatolarni moslash (5.10: Zod/DB status). Bu — 5.10 (global error handler) ning NestJS versiyasi.
HttpException iyerarxiyasi. NestJS'da barcha HTTP xatolar bitta bazaviy HttpException klassidan meros oladi — har biri tayyor status kodi bilan:
import {
HttpException, BadRequestException, UnauthorizedException,
ForbiddenException, NotFoundException, ConflictException,
InternalServerErrorException, HttpStatus,
} from "@nestjs/common";
throw new BadRequestException("Noto'g'ri so'rov"); // 400
throw new UnauthorizedException("Token kerak"); // 401
throw new ForbiddenException("Ruxsat yo'q"); // 403
throw new NotFoundException("Topilmadi"); // 404
throw new ConflictException("Allaqachon mavjud"); // 409
throw new InternalServerErrorException(); // 500
// Bazaviy — ixtiyoriy status
throw new HttpException("Maxsus xato", HttpStatus.I_AM_A_TEAPOT); // 418
HttpExceptiondaraxti:HttpException(baza)BadRequestException(400),UnauthorizedException(401),ForbiddenException(403),NotFoundException(404),ConflictException(409),UnprocessableEntityException(422),InternalServerErrorException(500) va h.k. Bularnithrowqilsangiz — NestJS built-in filter avtomatik to'g'ri status + JSON qaytaradi 8.1-bob. Filter ichidaexception instanceof HttpExceptionbilan aynan shu iyerarxiyani tekshirasiz (Misol 7).
Custom exception — biznesga xos xato (baza klassini kengaytirib):
// domain xatosi — o'z status va payload'i bilan
export class InsufficientBalanceException extends HttpException {
constructor(kerakli: number, mavjud: number) {
super(
{ xato: "Mablag' yetarli emas", kerakli, mavjud }, // response body (obyekt ham mumkin)
HttpStatus.PAYMENT_REQUIRED, // 402
);
}
}
throw new InsufficientBalanceException(100, 30);
// Javob: 402 { xato: "Mablag' yetarli emas", kerakli: 100, mavjud: 30 }Custom exception —
HttpException'niextendsqilib,super(response, status)chaqiring.response— string yoki obyekt (izchil payload). Bu — biznes xatosini 5.10-bob toza, tipli, qayta ishlatiladigan qiladi. Filter ularni ham avtomatik ushlaydi (meros —instanceof HttpException).
@Catch(...) — nishonli filter. @Catch() (parametrsiz) — hamma xato; @Catch(HttpException) — faqat HTTP xatolar; @Catch(PrismaClientKnownRequestError) — faqat Prisma DB xatolari (5.10 — DB xatoni statusga moslash):
import { Catch, ArgumentsHost, ExceptionFilter, ConflictException } from "@nestjs/common";
import { Prisma } from "@prisma/client";
@Catch(Prisma.PrismaClientKnownRequestError) // faqat Prisma xatolari
export class PrismaExceptionFilter implements ExceptionFilter {
catch(exception: Prisma.PrismaClientKnownRequestError, host: ArgumentsHost) {
const response = host.switchToHttp().getResponse();
if (exception.code === "P2002") { // unique constraint (5.10)
return response.status(409).json({ success: false, message: "Allaqachon mavjud" });
}
return response.status(500).json({ success: false, message: "DB xatosi" });
}
}Nishonli
@Catch: bitta filter bir necha aniq xato turini ham ushlaydi (@Catch(A, B)). Bir nechta filter bo'lsa — NestJS eng aniq mos filterni tanlaydi (Prisma xatosi PrismaFilter, qolgani global@Catch()). Bu — DB/tashqi xatolarni HTTP statusga toza moslash yo'li 5.10-bob.
2.9. Custom decorator + metadata (RBAC asosi)
import { SetMetadata } from "@nestjs/common";
// Custom dekorator (@Roles — 5.17, 7.6)
export const Roles = (...roles: string[]) => SetMetadata("roles", roles);
// Ishlatish
@Roles("admin") // metama'lumot qo'yadi
@UseGuards(RolesGuard) // guard o'qiydi (2.7)
@Delete(":id")
ochir() {}
// Custom param decorator (@CurrentUser — 7.6)
export const CurrentUser = createParamDecorator(
(data, ctx: ExecutionContext) => ctx.switchToHttp().getRequest().user,
);
@Get("me")
me(@CurrentUser() user: User) { return user; } // req.user (8.9)Custom decorator 7.6-bob + metadata:
SetMetadata— handler'ga ma'lumot biriktiradi (@Roles("admin")); guardReflectorbilan o'qiydi 2.7-bob — RBAC 8.7-bob.createParamDecorator— parametr dekorator (@CurrentUser()— req.user — 8.9). Bu — NestJS'ning deklarativ kuchi (auth/RBAC toza).
2.10. Bog'lash darajalari (global, controller, route)
// Global (main.ts — hamma)
app.useGlobalGuards(new AuthGuard());
app.useGlobalInterceptors(new LoggingInterceptor());
app.useGlobalFilters(new GlobalExceptionFilter());
// Controller (butun controller)
@UseGuards(AuthGuard)
@Controller("users")
export class UsersController {}
// Route (bitta metod)
@UseGuards(RolesGuard)
@Delete(":id")
ochir() {}Uch daraja: global (main.ts — hamma endpoint); controller (
@UseGuardsclass'da — butun controller); route (metod'da — bitta). Tartib: global controller route 2.11-bob. DI kerak bo'lsa — global'ni provider sifatida (APP_GUARD— 2.12).
2.11. Bajarilish tartibi (bir nechta)
Guard'lar (bir nechta): global controller route (bog'lash tartibida)
Interceptor'lar: oldin — globalcontrollerroute; keyin — TESKARI (FILO)
Interceptor "matryoshka" (RxJS — first in, last out — 2.5)Tartib: guard'lar — bog'lash tartibida (global birinchi). Interceptor'lar — oldin (tashqaridan ichkariga), keyin (ichkaridan tashqariga — FILO — RxJS). Bir nechta bo'lsa muhim (masalan auth guard roles guard).
2.12. DI bilan global (APP_GUARD)
// Global guard DI bilan (provider sifatida — 8.2)
import { APP_GUARD, APP_INTERCEPTOR, APP_FILTER } from "@nestjs/core";
@Module({
providers: [
{ provide: APP_GUARD, useClass: AuthGuard }, // global guard (DI ishlaydi)
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
{ provide: APP_FILTER, useClass: GlobalExceptionFilter },
],
})
export class AppModule {}
APP_GUARD/APP_INTERCEPTOR/APP_FILTER(provider sifatida) — global + DI ishlaydi (new AuthGuard()— DI yo'q; provider — DI bor). Guard'ga service inject kerak bo'lsa (JwtService — 8.9) — shu usul. Tavsiya (best practices): global enhancer'larni provider sifatida.
2.13. Real use-case'lar
Guard: auth (JWT — 8.9), RBAC (5.17, 8.7), rate limit, feature flag
Interceptor: javob formati 5.7-bob, logging/timing 5.12-bob, kesh 5.21-bob,
timeout, ClassSerializer (parolsiz — 8.5: 2.13)
Middleware: raw body, correlation ID 5.12-bob, CORS (lekin built-in afzal)
Filter: global xato 5.10-bob, Prisma/TypeORM xato status, log2.14. Guard vs Middleware (qachon qaysi)
Middleware: umumiy, ExecutionContext'siz (log, header)
Guard: AUTH/RUXSAT — ExecutionContext bilan (qaysi handler, metadata — RBAC)
Auth uchun GUARD (middleware emas):
- ExecutionContext (handler/metadata — RBAC)
- true/false (toza ruxsat mantig'i)
- DI (JwtService — 8.9)
- Pipe'dan oldin (validatsiyadan oldin ruxsat)Auth Guard (middleware emas): Express'da auth middleware edi 5.15-bob; NestJS'da — guard (ExecutionContext, metadata, true/false, DI). Middleware — faqat umumiy (log). Bu — NestJS'ning to'g'ri yo'li (8.9 auth — guard bilan).
2.15. Best practices (14)
Auth/RBAC — guard (middleware emas — 2.14)
Javob format/log/kesh — interceptor 2.5-bob
Global enhancer — APP_GUARD provider (DI — 2.12)
Custom decorator + metadata (@Roles — RBAC — 2.9)
Global exception filter (izchil xato — 5.10, 2.8)
Request lifecycle tartibini biling (guardpipehandler — 2.1)
ExecutionContext (handler/metadata — 2.7)
ClassSerializerInterceptor (parolsiz javob — 8.5)3. Sintaksis — tez ma'lumotnoma
// Guard (2.4)
@Injectable() class AuthGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean { return true; }
}
@UseGuards(AuthGuard)
// Interceptor (2.5)
@Injectable() class LogInterceptor implements NestInterceptor {
intercept(ctx, next: CallHandler) { return next.handle().pipe(map(d => ...)); }
}
@UseInterceptors(LogInterceptor)
// Middleware 2.3-bob: NestMiddleware, use(req, res, next)
// Filter 2.8-bob: @Catch() class implements ExceptionFilter { catch(exc, host) {} }
// Custom decorator 2.9-bob: SetMetadata("key", value); createParamDecorator
// Global DI 2.12-bob: { provide: APP_GUARD, useClass: X }4. Batafsil kod namunalari
Misol 1 — Auth Guard (JWT — 8.9 kirish — 2.4)
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from "@nestjs/common";
import { JwtService } from "@nestjs/jwt"; // (8.9)
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {} // DI (8.2)
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest(); // (2.7)
const token = request.headers.authorization?.split(" ")[1]; // "Bearer X" (5.15)
if (!token) throw new UnauthorizedException("Token kerak"); // 401 (8.1)
try {
const payload = await this.jwtService.verifyAsync(token); // (8.9)
request.user = payload; // req.user (8.9)
return true; // ruxsat
} catch {
throw new UnauthorizedException("Token noto'g'ri");
}
}
}Misol 2 — Roles Guard (RBAC — 5.17, 8.7 — 2.7, 2.9)
// roles.decorator.ts (custom decorator — 2.9)
import { SetMetadata } from "@nestjs/common";
export const ROLES_KEY = "roles";
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {} // DI (2.7)
canActivate(context: ExecutionContext): boolean {
const kerakliRollar = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(), // route metadata
context.getClass(), // controller metadata
]);
if (!kerakliRollar) return true; // rol talab qilinmagan
const { user } = context.switchToHttp().getRequest();
if (!kerakliRollar.includes(user.rol)) {
throw new ForbiddenException("Ruxsat yo'q"); // 403 (5.17)
}
return true;
}
}
// Ishlatish (auth + roles birga)
@UseGuards(AuthGuard, RolesGuard) // tartib: auth roles (2.11)
@Roles("admin")
@Delete(":id")
ochir(@Param("id") id: string) {}Misol 3 — Transform Interceptor (javob format — 5.7 — 2.6)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { Observable } from "rxjs";
import { map } from "rxjs/operators";
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, any> {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map((data) => ({ // standart format (5.7)
success: true,
data,
timestamp: new Date().toISOString(),
})),
);
}
}
// Har handler javobi avtomatik o'raladi: { success: true, data: ..., timestamp }Misol 4 — Logging Interceptor (timing — 5.12 — 2.5)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from "@nestjs/common";
import { tap } from "rxjs/operators";
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private logger = new Logger("HTTP"); // NestJS logger (8.15)
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
const { method, url } = req;
const boshlandi = Date.now();
return next.handle().pipe(
tap(() => { // KEYIN (javobni o'zgartirmasdan — 2.6)
this.logger.log(`${method} ${url} — ${Date.now() - boshlandi}ms`); // (5.12)
}),
);
}
}Misol 5 — Timeout Interceptor (2.5, 2.6)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, RequestTimeoutException } from "@nestjs/common";
import { Observable, throwError, TimeoutError } from "rxjs";
import { timeout, catchError } from "rxjs/operators";
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
timeout(5000), // 5 sekund (RxJS)
catchError((err) => {
if (err instanceof TimeoutError) {
return throwError(() => new RequestTimeoutException()); // 408
}
return throwError(() => err);
}),
);
}
}Misol 6 — Cache Interceptor (5.21 — 2.5)
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from "@nestjs/common";
import { of, Observable } from "rxjs";
import { tap } from "rxjs/operators";
@Injectable()
export class CacheInterceptor implements NestInterceptor {
constructor(private cache: RedisService) {} // DI (5.21)
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
const req = context.switchToHttp().getRequest();
const kalit = `cache:${req.url}`;
const keshlangan = await this.cache.get(kalit);
if (keshlangan) return of(JSON.parse(keshlangan)); // cache hit 5.21-bob — handler ishlamaydi
return next.handle().pipe(
tap((data) => this.cache.set(kalit, JSON.stringify(data), 60)), // keshla
);
}
}
// Production'da @nestjs/cache-manager CacheInterceptor (8.15)Misol 7 — Global Exception Filter (5.10 — 2.8)
import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus, Logger } from "@nestjs/common";
@Catch() // barcha xato
export class GlobalExceptionFilter implements ExceptionFilter {
private logger = new Logger("Exception");
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const request = ctx.getRequest();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message: any = "Ichki server xatosi";
if (exception instanceof HttpException) { // NestJS xato (8.1)
status = exception.getStatus();
message = exception.getResponse();
}
// 5xx — to'liq log 5.12-bob; 4xx — qisqa (5.10)
if (status >= 500) {
this.logger.error(`${request.method} ${request.url}`, (exception as Error).stack);
}
response.status(status).json({ // izchil format (5.7)
success: false,
statusCode: status,
message,
path: request.url,
timestamp: new Date().toISOString(),
});
}
}Misol 8 — Custom param decorator (@CurrentUser — 2.9)
import { createParamDecorator, ExecutionContext } from "@nestjs/common";
// req.user'ni olish (auth guard qo'ygan — Misol 1)
export const CurrentUser = createParamDecorator(
(data: keyof User | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user; // @CurrentUser("id") yoki @CurrentUser()
},
);
// Ishlatish (toza — req.user o'rniga)
@UseGuards(AuthGuard)
@Get("me")
me(@CurrentUser() user: User) { // to'liq user
return user;
}
@Get("my-id")
myId(@CurrentUser("id") userId: number) { // faqat id
return { userId };
}Misol 9 — Global enhancer'lar (DI — 2.12)
// app.module.ts — global enhancer'lar (DI ishlaydi — 2.12)
import { APP_GUARD, APP_INTERCEPTOR, APP_FILTER } from "@nestjs/core";
@Module({
providers: [
{ provide: APP_GUARD, useClass: AuthGuard }, // global auth (DI — JwtService)
{ provide: APP_INTERCEPTOR, useClass: ResponseInterceptor }, // global javob format
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor }, // global log
{ provide: APP_FILTER, useClass: GlobalExceptionFilter }, // global xato
],
})
export class AppModule {}
// Public endpoint uchun @Public() dekorator + guard'da tekshirish (2.9)Misol 10 — @Public dekorator (global guard istisno — 2.9)
// public.decorator.ts
export const IS_PUBLIC_KEY = "isPublic";
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
// AuthGuard — public endpoint'larni o'tkazib yuborish
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService, private reflector: Reflector) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(), context.getClass(),
]);
if (isPublic) return true; // public — token shart emas
// ... token tekshir (Misol 1) ...
return true;
}
}
// Ishlatish — global auth, lekin login/signup public
@Public()
@Post("login")
login(@Body() dto: LoginDto) {} // token shart emas (global guard o'tkazadi)5. To'g'ri va noto'g'ri holatlar
1) Auth uchun middleware (guard o'rniga)
auth middleware (ExecutionContext yo'q — 2.14)
auth guard (metadata, DI, true/false)2) Global guard new bilan (DI kerak bo'lsa)
// DI ishlamaydi (JwtService inject bo'lmaydi)
app.useGlobalGuards(new AuthGuard());
// APP_GUARD provider (DI — 2.12)
{ provide: APP_GUARD, useClass: AuthGuard }3) Interceptor'da Observable qaytarmaslik
// next.handle() qaytarilmadi (handler ishlamaydi — 2.5)
intercept(ctx, next) { next.handle(); }
// return + pipe
intercept(ctx, next) { return next.handle().pipe(map(...)); }4) Xatoni har controller'da qo'lda
har joyda try/catch + format (takror — 5.10)
global exception filter (bir joyda — 2.8)5) req.user'ni to'g'ridan (custom decorator o'rniga)
// req.user (tursiz, takror)
@Req() req req.user
// @CurrentUser (toza, tipli — 2.9)
@CurrentUser() user: User6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Guard DI ishlamaydi (global)
Sababi: new Guard() — DI yo'q 2.12-bob. Yechimi: APP_GUARD provider.
Xato 2 — Interceptor javobni o'zgartirmaydi
Sababi: next.handle() qaytarilmagan yoki pipe yo'q 2.5-bob. Yechimi: return next.handle().pipe(map(...)).
Xato 3 — Reflector metadata undefined
Sababi: custom dekorator (@Roles) qo'yilmagan, yoki getHandler/getClass 2.7-bob. Yechimi: dekorator qo'shing; getAllAndOverride (handler + class).
Xato 4 — Global guard public endpoint'ni bloklaydi
Sababi: @Public istisnosi yo'q 2.9-bob. Yechimi: @Public dekorator + guard'da tekshirish (Misol 10).
Xato 5 — Exception filter ishlamaydi
Sababi: @Catch noto'g'ri, yoki global ulanmagan 2.8-bob. Yechimi: @Catch(); APP_FILTER yoki useGlobalFilters.
Xato 6 — Guard tartibi noto'g'ri (roles auth'dan oldin)
Sababi: @UseGuards tartibi 2.11-bob. Yechimi: auth birinchi (@UseGuards(AuthGuard, RolesGuard)).
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Request lifecycle (8.1: 2.12): qatlamlar tartibi.
- Pipe 8.5-bob: validatsiya (shu oilada).
- Auth (8.9, 5.15): AuthGuard (JWT).
- RBAC (8.7, 5.17): RolesGuard + @Roles.
- Error handling 5.10-bob: exception filter.
- Logger 5.12-bob: logging interceptor.
- Kesh (5.21, 8.15): cache interceptor.
- Decorators 7.6-bob: custom dekorator, metadata.
- DI 8.2-bob: APP_GUARD, Reflector.
- RxJS: interceptor (map/tap).
8. Eng yaxshi amaliyotlar (best practices)
- Auth/RBAC — guard (middleware emas — ExecutionContext/DI — 2.14).
- Javob format/log/kesh — interceptor (RxJS — 2.5).
- Global enhancer — APP_GUARD provider (DI — 2.12).
- Custom decorator + metadata (@Roles/@Public/@CurrentUser — 2.9).
- Global exception filter (izchil xato — 5.10, 2.8).
- Request lifecycle tartibini biling (guardpipehandler — 2.1).
- ExecutionContext (handler/metadata — Reflector — 2.7).
- Guard tartibi (auth roles — 2.11).
- @Public (global guard istisno — 2.9, Misol 10).
- ClassSerializerInterceptor (parolsiz javob — 8.5).
9. Amaliy loyiha: "Cross-Cutting Qatlam (Guard/Interceptor/Filter)"
NestJS enhancer'larni mustahkamlash.
Maqsad
To'liq cross-cutting qatlamni qurish: auth/roles guard, transform/log interceptor, global exception filter, custom decorator.
Talablar (requirements)
- Auth Guard: JWT tekshirish; req.user (Misol 1, 2.4).
- Roles Guard + @Roles: RBAC; Reflector metadata (Misol 2, 2.7, 2.9).
- Transform Interceptor: standart javob format (Misol 3, 2.6).
- Logging Interceptor: timing (Misol 4, 2.5).
- Exception Filter: global, izchil xato, log (Misol 7, 2.8).
- Custom param decorator: @CurrentUser (Misol 8, 2.9).
- @Public: global guard istisno (Misol 10, 2.9).
- Global enhancer'lar: APP_GUARD/INTERCEPTOR/FILTER (DI — Misol 9, 2.12).
- Tartib: auth roles; request lifecycle (2.1, 2.11).
- (Bonus) Timeout/Cache interceptor (Misol 5, 6).
Maslahatlar (hint)
- Auth guard (middleware emas — 2.14, 1-xato).
- Global DI APP_GUARD provider (2.12, 1-xato).
- Interceptor: return next.handle().pipe (2.5, 3-xato).
- Metadata: Reflector.getAllAndOverride (2.7, 3-xato).
- @Public + guard tekshiruvi (2.9, 4-xato).
- Guard tartibi: auth birinchi (2.11, 6-xato).
"Tayyor" mezonlari (acceptance criteria)
- Auth Guard (JWT, req.user).
- Roles Guard + @Roles (RBAC).
- Transform Interceptor (format).
- Logging Interceptor (timing).
- Exception Filter (global, log).
- @CurrentUser decorator.
- @Public (istisno).
- Global enhancer'lar (DI).
- Tartib to'g'ri (authroles).
- (Bonus) Timeout/Cache.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda NestJS'ning cross-cutting mexanizmlarini chuqur o'rgandik:
- Request lifecycle (Middleware Guard Interceptor Pipe Handler Interceptor Filter — 2.1); qaysi mexanizm qaysi vazifa 2.2-bob.
- Middleware (umumiy — 2.3); Guard (ruxsat — CanActivate — 2.4); Interceptor (o'rash — RxJS map/tap — 2.5, 2.6); Exception Filter (xato — 2.8).
- ExecutionContext (handler/metadata — Reflector — 2.7); custom decorator (@Roles/@CurrentUser/@Public — 2.9).
- Bog'lash (global/controller/route — 2.10); tartib 2.11-bob; APP_GUARD (DI — 2.12); guard vs middleware 2.14-bob.
Keyingi bob — 8.7-bob: User–Role–Auth, RBAC (ruxsatlarni boshqarish). Guard/interceptor mexanizmini bildik; endi ularni ishlatib, NestJS'da to'liq RBAC (rol asosida ruxsat) tizimini quramiz: user-role-permission, RolesGuard, dinamik ruxsatlar, ownership. Bu — 5.17 (RBAC) ning NestJS'dagi to'liq amaliyoti.
Foydalanilgan rasmiy/ishonchli manbalar
- docs.nestjs.com — Guards, Interceptors, Middleware, Exception filters, Custom decorators
- docs.nestjs.com/faq/request-lifecycle — to'liq tartib; Reflector, ExecutionContext
- rxjs.dev — Observable, operatorlar (map, tap, catchError, timeout)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!