Middleware
Middleware nima?
Tasavvur qiling: siz katta bir ofisga kiryapsiz. Kirish eshigida xavfsizlik xodimi turibdi. U har bir kelgan odamni tekshiradi — hujjatni ko'radi, ro'yxatga yozadi, keyin ichkariga o'tkazadi. Ba'zilarni o'tkazmaydi. Bu xavfsizlik xodimi — Middleware.
NestJS da Middleware — so'rov Controller ga yetib borishidan oldin ishlaydigan funksiya. U so'rovni ko'radi, xohlasa o'zgartiradi, keyin keyingi bosqichga o'tkazadi yoki to'xtatadi.
Foydalanuvchi so'rovi
↓
[ Middleware ] ← tekshirish, loglash, o'zgartirish
↓
[ Controller ]
↓
[ Service ]
↓
Javob
Middleware va boshqa tushunchalar farqi
NestJS da bir nechta o'xshash tushunchalar bor — Middleware, Guard, Interceptor. Bularni aralashtirmaslik kerak:
Middleware — so'rov kelganida eng birinchi ishlaydi. Request va Response ga to'liq kirish imkoni bor. Express Middleware bilan bir xil.
Guard — foydalanuvchining ruxsati bormi yo'qmi — tekshiradi. Keyingi darslarda o'rganamiz.
Interceptor — so'rov ham, javob ham kelganda ishlaydi. Ikkala tomonda ham o'zgartirish qilishi mumkin.
Middleware eng sodda va eng erta ishlaydi. Shuning uchun loglash, so'rovni tekshirish, header qo'shish kabi ishlar uchun ishlatiladi.
Birinchi Middleware
CLI bilan yaratish:
nest g middleware common/middleware/logger --no-spec
// common/middleware/logger.middleware.ts
import { Injectable, NestMiddleware, Logger } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
private readonly logger = new Logger("HTTP");
use(req: Request, res: Response, next: NextFunction) {
// So'rov keldi — loglaymiz
const { method, originalUrl } = req;
const start = Date.now();
this.logger.log(`→ ${method} ${originalUrl}`);
// So'rov tugaganda javobni loglaymiz
res.on("finish", () => {
const duration = Date.now() - start;
const { statusCode } = res;
this.logger.log(
`← ${method} ${originalUrl} ${statusCode} — ${duration}ms`,
);
});
// Keyingi bosqichga o'tkazamiz
next();
}
}
Bu yerda uchta parametr bor — ularni to'liq tushunish muhim:
req — Request ob'ekti. Foydalanuvchidan kelgan hamma narsa shu yerda: URL, method, header lar, body, cookie lar. Siz buni o'qiy olasiz, o'zgartira olasiz.
res — Response ob'ekti. Foydalanuvchiga qaytariladigan javob. Siz uni to'xtatib, o'zingiz javob qaytarishingiz mumkin.
next — funksiya. Uni chaqirsangiz — so'rov keyingi bosqichga o'tadi. Chaqirmasangiz — so'rov bu yerda qolib ketadi, foydalanuvchi javob olmaydi. Bu juda muhim — next() ni unutmang.
Middleware ni ulash
Middleware ni Module da ulanadi. Lekin boshqa narsalardan farqli — @Module decorator ichida emas, balki configure metodi orqali:
// app.module.ts
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
import { LoggerMiddleware } from "./common/middleware/logger.middleware";
import { UsersModule } from "./users/users.module";
@Module({
imports: [UsersModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware) // qaysi Middleware
.forRoutes("*"); // qaysi route larga
}
}
NestModule — bu interfeys. Uni implement qilganda configure metodini yozish shart. NestJS bu metodga MiddlewareConsumer ob'ektini beradi — u orqali Middleware ni qayerga ulashni belgilaysiz.
forRoutes('*') — barcha route larga qo'llash. * — wildcard, ya'ni hamma narsa.
Terminalda endi har bir so'rov loglanadi:
[HTTP] → GET /users
[HTTP] ← GET /users 200 — 12ms
[HTTP] → POST /users
[HTTP] ← POST /users 201 — 45ms
forRoutes — qaysi route larga qo'llash
Middleware ni barcha route larga emas, faqat ma'lum route larga qo'llash mumkin. Bir nechta usul bor:
String bilan:
consumer.apply(LoggerMiddleware).forRoutes("users"); // faqat /users va uning ostidagi route lar
Metod bilan birga:
import { RequestMethod } from "@nestjs/common";
consumer.apply(LoggerMiddleware).forRoutes({
path: "users",
method: RequestMethod.GET, // faqat GET so'rovlari
});
Controller bilan:
import { UsersController } from "./users/users.controller";
consumer.apply(LoggerMiddleware).forRoutes(UsersController); // faqat UsersController dagi route lar
Controller bilan ishlatish eng qulay — route larni qo'lda yozishingiz shart emas. Controller o'zgarganda Middleware avtomatik yangi route larni ham qamrab oladi.
exclude — ba'zi route larni o'tkazib yuborish
Ba'zan Middleware ni deyarli barcha route larga qo'llashingiz kerak, lekin bir-ikkitasini istisno qilishingiz kerak. Masalan, health check endpoint i uchun loglash shart emas:
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: "health", method: RequestMethod.GET },
{ path: "metrics", method: RequestMethod.GET },
)
.forRoutes("*");
exclude — bu route larga Middleware qo'llanmaydi. Qolgan barcha route larga qo'llanadi.
Bir nechta Middleware
apply ga bir nechta Middleware berilishi mumkin. Ular chapdan o'ngga ketma-ket ishlaydi:
consumer.apply(LoggerMiddleware, AuthMiddleware, CorsMiddleware).forRoutes("*");
Avval LoggerMiddleware ishlaydi, keyin AuthMiddleware, keyin CorsMiddleware. Har biri next() chaqirsa — keyingisi boshlanadi. Birontasi next() chaqirmasa — keyingisi hech qachon ishlamaydi.
Funksional Middleware
Har doim sinf yozish shart emas. Agar Middleware oddiy va dependency injection kerak bo'lmasa — oddiy funksiya yozish mumkin:
// common/middleware/cors.middleware.ts
import { Request, Response, NextFunction } from "express";
export function corsMiddleware(
req: Request,
res: Response,
next: NextFunction,
) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,PATCH");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization");
next();
}
Ulash xuddi sinf Middleware singari:
consumer.apply(corsMiddleware).forRoutes("*");
Sinf va funksiya Middleware ning farqi bitta — sinf @Injectable() bilan belgilanadi va constructor da dependency injection ishlatishi mumkin. Funksiya — yo'q.
Amaliy misol — so'rovni boyitish
Middleware faqat loglash uchun emas. Ba'zan so'rovga qo'shimcha ma'lumot qo'shiladi. Masalan, so'rov kelgan vaqtni qo'shish:
// common/middleware/request-time.middleware.ts
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response, NextFunction } from "express";
@Injectable()
export class RequestTimeMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// So'rov ob'ektiga yangi maydon qo'shamiz
(req as any).requestTime = new Date().toISOString();
next();
}
}
Controller da bu ma'lumotni olish:
@Get()
findAll(@Req() req: Request) {
return {
data: [],
requestTime: (req as any).requestTime,
};
}
Javob:
{
"data": [],
"requestTime": "2024-01-15T10:30:00.000Z"
}
Xulosa
Middleware — so'rov va Controller orasidagi "darvoza qo'riqchisi". U so'rovni ko'radi, tekshiradi, o'zgartiradi yoki to'xtatadi. Eng muhimi — next() chaqirilmasa so'rov keyinga o'tmaydi.
Eslab qoling:
use(req, res, next)— uchala parametr har doim bo'ladinext()— chaqirilmasa so'rov qolib ketadiconfiguremetodi — Middleware ni Module da ulash joyiforRoutes— qaysi route larga qo'llashexclude— qaysi route larni o'tkazib yuborish- Bir nechta Middleware — chapdan o'ngga ketma-ket ishlaydi
- Sinf Middleware — DI ishlatishi mumkin
- Funksional Middleware — DI shart bo'lmagan oddiy holatlar uchun
Keyingi dars
9-dars: Guards — Authorization, Role-based