8.15-bob: Caching (Redis), Logger, Error handling
8-QISM — NestJS (chuqur) · 15-mavzu
1. Kirish va motivatsiya
Config'ni 8.14-bob bildik. Endi production ilovaning ishlash (tezlik), kuzatuv, barqarorlik uchun zarur uchta mavzuni — caching (Redis), logger, error handling — NestJS'da chuqur ko'ramiz. 5.21'da Redis caching'ni, 5.12'da logger'ni, 5.10'da error handling'ni Express'da o'rgandik; endi NestJS'da — CacheModule, structured logger (Pino/Winston), va global exception filter 8.6-bob bilan, professional darajada. Bu uch mavzu — havaskor va production ilovani ajratadi: tez (cache), ko'rinadigan (log), barqaror (xato boshqaruv).
Caching 5.21-bob — tez-tez so'raladigan, kam o'zgaradigan ma'lumotni xotirada saqlash (DB'ga har safar bormaslik). NestJS CacheModule — bir xil kod bilan in-memory yoki Redis (taqsimlangan — 5.21). CacheInterceptor 8.6-bob — javobni avtomatik kesh. Logger 5.12-bob — nima sodir bo'layotganini yozish (debug, monitoring); production'da structured (JSON — Pino/Winston). Error handling 5.10-bob — xatoni markazlashtirilgan ushlash (global exception filter — 8.6), toza javob, log.
Bu bob: CacheModule (Redis), cache interceptor, cache strategiyalar 5.21-bob, Pino/Winston logger, structured logging, global exception filter 8.6-bob, xato turlari — chuqur. Bu uch mavzu 5.21, 5.12, 5.10, 8.6 ni NestJS production darajasiga olib chiqadi. Bu — ilovaning tezligi, ko'rinishi, barqarorligi.
O'xshatish: cache — oshxonadagi tayyor taomlar (5.21: do'kon javoni) — har buyurtmaga noldan pishirmaysiz (DB), tayyorini berasiz (tez). Logger — qora quti (samolyot) — har voqea yoziladi (xato bo'lsa, nima bo'lganini bilasiz). Error handling — xavfsizlik tizimi — nosozlik butun binoni yiqitmaydi (bir xato butun ilovani o'chirmaydi), markazda ushlanadi, log qilinadi, foydalanuvchiga toza xabar. Uchalasi — professional ilovaning poydevori.
Nega muhim?
- Tezlik — cache (DB yuki kamayadi — 5.21).
- Ko'rinish — log (xatoni topish, monitoring — 5.12).
- Barqarorlik — error handling (ilova yiqilmaydi — 5.10).
- Production — bularsiz jiddiy ilova yo'q.
2. Nazariya — chuqur tushuntirish
2.1. CacheModule sozlash (in-memory)
import { CacheModule } from "@nestjs/cache-manager";
@Module({
imports: [
CacheModule.register({ // in-memory (5.21)
isGlobal: true, // butun ilovaga
ttl: 60000, // 60 soniya (ms)
max: 100, // maks element
}),
],
})
export class AppModule {}CacheModule:
register(in-memory — boshlash uchun),ttl(yashash vaqti — ms),max(maks element).isGlobal(har joyda). Boshlanishiga in-memory, keyin Redisga o'tish — kod o'zgarmaydi 2.2-bob. Bu — 5.21 cache'ning NestJS abstraksiyasi.
2.2. Redis store (taqsimlangan — 5.21)
import { CacheModule } from "@nestjs/cache-manager";
import { redisStore } from "cache-manager-redis-yet";
CacheModule.registerAsync({
isGlobal: true,
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
store: await redisStore({ // Redis (5.21)
socket: { host: config.get("REDIS_HOST"), port: config.get("REDIS_PORT") },
}),
ttl: 60000,
}),
});Redis store 5.21-bob: in-memory'dan Redis'ga — faqat
storeo'zgaradi (kod o'zgarmaydi — abstraksiya foydasi). Redis — taqsimlangan (ko'p instance bir cache — 5.21: 2.3; in-memory har instance alohida). Production'da Redis (gorizontal masshtab — 10). ConfigService bilan 8.14-bob.
2.3. CacheInterceptor (avtomatik kesh)
import { CacheInterceptor, CacheKey, CacheTTL } from "@nestjs/cache-manager";
@Controller("products")
@UseInterceptors(CacheInterceptor) // avtomatik kesh (8.6)
export class ProductsController {
@Get()
@CacheKey("barcha_mahsulot") // maxsus kalit
@CacheTTL(300000) // 5 daqiqa
hammasi() {
return this.service.hammasi(); // natija keshlanadi
}
}CacheInterceptor (8.6 interceptor) — GET javobini avtomatik keshlaydi (kalit — route + parametr).
@CacheKey(maxsus kalit),@CacheTTL(vaqt). Faqat GET (mutatsiya emas). Eng oson cache (kod kam). Global ham (APP_INTERCEPTOR). Bu — 5.21 cache'ning deklarativ usuli.
2.4. Qo'lda cache (CACHE_MANAGER — nazorat)
import { CACHE_MANAGER } from "@nestjs/cache-manager";
import { Cache } from "cache-manager";
@Injectable()
export class ProductsService {
constructor(@Inject(CACHE_MANAGER) private cache: Cache) {} // DI
async bitta(id: string) {
const kalit = `product_${id}`;
const keshda = await this.cache.get(kalit); // cache tekshir (5.21)
if (keshda) return keshda; // cache hit
const mahsulot = await this.repo.findById(id); // cache miss DB
await this.cache.set(kalit, mahsulot, 300000); // cache'ga (5 daqiqa)
return mahsulot;
}
async yangila(id: string, dto: any) {
const natija = await this.repo.update(id, dto);
await this.cache.del(`product_${id}`); // o'zgarganda — invalidatsiya (5.21: 2.9)
return natija;
}
}Qo'lda cache (CACHE_MANAGER — to'liq nazorat):
cache.get/set/del. Cache-aside naqsh (5.21: 2.5): tekshir yo'q bo'lsa DB cache'ga. Invalidatsiya (o'zgargandadel— 5.21: 2.9 — eng muhim/qiyin). Murakkab cache mantiq uchun (interceptor yetmaganda). Bu — 5.21 strategiyasi.
2.5. Cache strategiyalari va invalidatsiya (5.21)
STRATEGIYALAR 5.21-bob:
- Cache-aside (lazy): ilova boshqaradi (get/miss/set — 2.4) — eng keng
- Write-through: yozishda cache + DB birga
- TTL: vaqt o'tib eskirish (oddiy invalidatsiya)
INVALIDATSIYA (5.21: 2.9 — eng qiyin):
- O'zgarganda del (update/delete cache.del)
- TTL (avtomatik eskirish)
- Tag/pattern (bog'liq keshlar)
"Eng qiyin 2 narsa: cache invalidatsiya va nomlash" (mashhur ibora)Cache strategiyalar 5.21-bob: cache-aside (eng keng — 2.4), write-through, TTL. Invalidatsiya (5.21: 2.9) — eng qiyin: o'zgarganda eski cache'ni o'chirish (aks holda eski ma'lumot). Usul:
del(o'zgarganda), TTL (vaqt). Eski ma'lumot xavfi — invalidatsiya muhim. Nimani keshlash: tez-tez o'qiladi + kam o'zgaradi (5.21: 2.4).
2.6. NestJS Logger (built-in)
import { Logger } from "@nestjs/common";
@Injectable()
export class OrdersService {
private readonly logger = new Logger(OrdersService.name); // kontekst nomi
async yarat(dto: any) {
this.logger.log(`Buyurtma yaratilmoqda: ${dto.userId}`); // info
try {
const order = await this.repo.save(dto);
this.logger.log(`Buyurtma yaratildi: ${order.id}`);
return order;
} catch (e) {
this.logger.error(`Buyurtma xatosi: ${e.message}`, e.stack); // xato + stack
throw e;
}
}
}NestJS Logger (built-in — 5.12):
new Logger(kontekst). Darajalar:log(info),error(stack bilan),warn,debug,verbose. Kontekst nomi (qaysi service). Konsolda rangli (dev). Production uchun structured (JSON — Pino/Winston — 2.7). Bu — 5.12 logger'ning NestJS asosi.
2.6-b. LoggerService interfeysi (custom logger)
import { LoggerService, Injectable } from "@nestjs/common";
// NestJS built-in Logger o'rniga o'z logger'ingizni ulash uchun
// LoggerService interfeysini implementatsiya qilasiz (5.12)
@Injectable()
export class MeningLogger implements LoggerService {
log(message: any, ...optional: any[]) { // info darajasi
this.yoz("LOG", message, optional);
}
error(message: any, ...optional: any[]) { // xato + stack
this.yoz("ERROR", message, optional);
}
warn(message: any, ...optional: any[]) {
this.yoz("WARN", message, optional);
}
debug?(message: any, ...optional: any[]) { // ixtiyoriy (?)
this.yoz("DEBUG", message, optional);
}
verbose?(message: any, ...optional: any[]) { // ixtiyoriy
this.yoz("VERBOSE", message, optional);
}
private yoz(daraja: string, message: any, meta: any[]) {
// JSON structured (5.12: 2.6) — o'z formatingiz, o'z transportingiz
process.stdout.write(JSON.stringify({
daraja,
vaqt: new Date().toISOString(),
xabar: message,
meta,
}) + "\n");
}
}
// main.ts — NestJS'ga o'z logger'ingizni bering
const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.useLogger(new MeningLogger()); // built-in o'rnigaLoggerService interfeysi 5.12-bob: NestJS built-in
Loggerni o'z logger'ingiz bilan almashtirish uchunLoggerServiceinterfeysini implementatsiya qilasiz (log/error/warn/debug?/verbose?—?— ixtiyoriy metodlar).app.useLogger(...)— butun ilova (framework ichki loglari ham) shu logger'dan o'tadi.bufferLogs: true— logger tayyor bo'lguncha loglarni bufferga oladi (dastlabki loglar yo'qolmaydi). Amaliyotda o'zingiz yozmaysiz — Pino/Winston adapterini ulaysiz 2.7-bob, lekin interfeysni bilish muhim (moslashuv).
2.7. Pino/Winston (structured logging — 5.12)
// nestjs-pino (tez, structured JSON)
import { LoggerModule } from "nestjs-pino";
LoggerModule.forRoot({
pinoHttp: {
level: process.env.NODE_ENV === "production" ? "info" : "debug",
transport: process.env.NODE_ENV !== "production"
? { target: "pino-pretty" } // dev: chiroyli rangli
: undefined, // prod: JSON (5.12: 2.6)
redact: ["req.headers.authorization", "*.parol"], // sirlarni yashirish (14)
},
});Pino/Winston (5.12 — structured): production'da JSON log (parse qilinadi — monitoring, ELK/Grafana — 5.12: 2.6).
nestjs-pino(tez, har log'da request kontekst).pino-pretty(dev — rangli).redact— sirlarni yashirish (parol, token log'ga tushmasin — 14 — muhim!). Pino — tez (async — event loop bloklanmaydi). Bu — 5.12'ning production amaliyoti.
Winston alternativasi (nest-winston) — Pino'dan boyroq konfiguratsiya (ko'p transport: fayl, konsol, tashqi servis — 5.12), lekin biroz sekinroq:
// nest-winston (moslashuvchan transportlar — 5.12)
import { WinstonModule } from "nest-winston";
import * as winston from "winston";
WinstonModule.forRoot({
level: process.env.NODE_ENV === "production" ? "info" : "debug",
format: winston.format.combine( // strukturaviy format
winston.format.timestamp(),
winston.format.json(), // JSON (prod — 2.6)
),
transports: [
new winston.transports.Console(), // konsol
new winston.transports.File({ // faylga (rotatsiya bilan)
filename: "logs/error.log",
level: "error", // faqat xatolar
}),
new winston.transports.File({ filename: "logs/combined.log" }),
],
});
// main.ts: app.useLogger(app.get(WINSTON_MODULE_NEST_PROVIDER));Pino vs Winston 5.12-bob: Pino — tezlik ustuvor (yuqori trafik, async, minimal ortiqcha yuk) — ko'pincha birinchi tanlov. Winston — moslashuvchanlik ustuvor (ko'p transport: fayl rotatsiyasi, Elasticsearch, Slack; boy format). Ikkalasi ham
useLoggerorqali NestJS built-in Logger'ni almashtiradi — service kodingizdagithis.logger.log(...)o'zgarmaydi (abstraksiya). Odatiy tanlov: Pino (agar maxsus transport zarur bo'lmasa).
2.8. Log darajalari va nima loglash (5.12)
DARAJALAR 5.12-bob:
error — xatolar (darrov e'tibor)
warn — ogohlantirish (potentsial muammo)
info — muhim hodisa (login, buyurtma, to'lov)
debug — batafsil (faqat dev)
NIMA LOGLASH:
So'rov (method, path, status, vaqt) — interceptor 8.6-bob
Muhim biznes hodisa (to'lov, ro'yxat)
Xatolar (stack bilan)
Sirlar (parol, token, karta — redact! 14)
Har mayda narsa (shovqin — muhimni yashiradi)Log darajalari 5.12-bob: error/warn/info/debug. Nima loglash: so'rov (interceptor — 8.6), muhim hodisa (to'lov), xato (stack). Sirlarni loglama (parol/token — redact — 14). Mayda shovqin loglama (muhimni ko'mib qo'yadi). Production'da info+, dev'da debug. To'g'ri log — muammoni tez topish.
2.9. Global Exception Filter (8.6 — markaziy xato)
@Catch() // barcha xato (8.6)
export class GlobalExceptionFilter implements ExceptionFilter {
private logger = new Logger("Exception");
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception instanceof HttpException
? exception.getStatus() : 500; // HTTP yoki 500
const message = exception instanceof HttpException
? exception.getResponse() : "Server xatosi";
this.logger.error(`${request.method} ${request.url} ${status}`, // log (5.12)
(exception as Error).stack);
response.status(status).json({ // toza javob (5.10)
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
}
}Global Exception Filter (8.6, 5.10) — barcha xatoni markazda ushlaydi (
@Catch()). HTTP xato status; boshqa 500. Log (stack — 5.12) + toza javob (foydalanuvchiga — 5.10: 2.7). Bu — xato boshqaruvning yuragi: ilova yiqilmaydi, xato ko'rinadi, javob izchil.APP_FILTER(global — 8.6: 2.12).
2.9-b. HttpAdapterHost (platforma-agnostik javob)
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus } from "@nestjs/common";
import { HttpAdapterHost } from "@nestjs/core";
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
// HttpAdapterHost — DI orqali (Express/Fastify farqini yashiradi)
constructor(private readonly httpAdapterHost: HttpAdapterHost) {}
catch(exception: unknown, host: ArgumentsHost) {
// response.json() emas — adapter orqali (platformadan mustaqil)
const { httpAdapter } = this.httpAdapterHost; // to'g'ri adapter
const ctx = host.switchToHttp();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const javob = { // izchil format (5.10)
statusCode: status,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(ctx.getRequest()), // adapter — URL
message: exception instanceof HttpException
? exception.getResponse()
: "Server xatosi",
};
// Express'da response.status().json(), Fastify'da reply.code().send() —
// adapter avtomatik to'g'ri metodni chaqiradi
httpAdapter.reply(ctx.getResponse(), javob, status);
}
}HttpAdapterHost 8.6-bob — NestJS ikki HTTP platformada ishlaydi: Express (odatiy) va Fastify (tezroq). Ularning
responseobyekti farq qiladi (res.json()vsreply.send()). Agar filterda to'g'ridan-to'g'riresponse.status().json()yozsangiz — kod faqat Express'ga bog'lanadi.HttpAdapterHostDI orqali to'g'ri adapterni beradi (httpAdapter.reply(...),getRequestUrl(...)) — kod platformadan mustaqil bo'ladi. Muhim nuqta: exception filter juda erta (ilova to'liq ishga tushmasdan) ishlashi mumkin, shu boisresponseobyektini@Inject(HttpAdapterHost)orqali olish — rasmiy tavsiya etilganAllExceptionsFilternaqshi (kutubxona xatolari ham to'g'ri ushlanadi).
2.10. Xato turlari va HTTP status (8.1, 5.10)
// NestJS built-in HTTP exception'lar (8.1)
throw new BadRequestException("Noto'g'ri ma'lumot"); // 400
throw new UnauthorizedException("Kirish kerak"); // 401
throw new ForbiddenException("Ruxsat yo'q"); // 403
throw new NotFoundException("Topilmadi"); // 404
throw new ConflictException("Email band"); // 409
throw new InternalServerErrorException(); // 500
// Custom exception
export class InsufficientStockException extends HttpException {
constructor(mahsulot: string) {
super(`${mahsulot}: zaxira yetarli emas`, HttpStatus.BAD_REQUEST);
}
}Xato turlari (8.1, 5.10): NestJS built-in exception'lar (to'g'ri HTTP status — 400/401/403/404/409/500). Custom exception (domenga xos —
InsufficientStock). To'g'ri status — RESTful 5.7-bob, frontend to'g'ri ishlov beradi. Exception filter 2.9-bob bularni ushlaydi. Operatsion vs dasturchi xatosi (5.10: 2.3).
2.11. Logging interceptor (so'rov kuzatuvi — 8.6)
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private logger = new Logger("HTTP");
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
const boshlanish = Date.now();
return next.handle().pipe(
tap(() => { // javobdan keyin (8.6)
const vaqt = Date.now() - boshlanish;
this.logger.log(`${req.method} ${req.url} — ${vaqt}ms`); // so'rov + vaqt
}),
);
}
}
// APP_INTERCEPTOR — global (har so'rov loglanadi — 8.6: 2.12)Logging interceptor 8.6-bob — har so'rovni loglaydi (method, URL, vaqt — sekin endpoint topish).
tap(RxJS — javobdan keyin — 8.6: 2.6). Global (APP_INTERCEPTOR). Bu — monitoring asosi (so'rov kuzatuvi). Pino bilan avtomatik 2.7-bob.
2.12. Cache + tezlik (production)
Cache foydasi 5.21-bob:
DB so'rov: 50ms cache: 1ms (50x tez)
DB yuki: kamayadi (ko'p o'qish cache'dan)
Nimani keshlash:
Mahsulot katalogi (kam o'zgaradi, ko'p o'qiladi)
Konfiguratsiya, statik ma'lumot
Og'ir hisoblash natijasi (aggregation — 8.13)
Tez o'zgaradigan (balans, real-time)
Foydalanuvchiga xos maxfiy (ehtiyot — 14)Cache tezligi 5.21-bob: DB (50ms) cache (1ms). DB yuki kamayadi (masshtab — 10). Keshlash: kam o'zgaradi + ko'p o'qiladi (katalog, aggregation natija). Keshlamaslik: tez o'zgaradigan (balans), maxfiy (14). To'g'ri cache — sezilarli tezlik (production muhim).
2.13. Health check va monitoring (10 ko'prik)
// @nestjs/terminus — health check
@Controller("health")
export class HealthController {
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck("database"), // DB ulanish
() => this.redis.checkHealth("redis"), // Redis
]);
}
}Health check (
@nestjs/terminus) — ilova "sog'lig'i" (DB, Redis ulanganmi)./healthendpoint — monitoring/load balancer tekshiradi (10 — Kubernetes liveness/readiness). Production'da majburiy (avtomatik qayta ishga tushirish). Log + health + metrics — to'liq kuzatuv (observability).
2.14. Best practices (cache/log/error)
CACHE:
Redis production (taqsimlangan — 2.2); to'g'ri narsani keshlash 2.12-bob
Invalidatsiya (o'zgarganda del — 2.5); TTL
LOGGER:
Structured JSON prod (Pino/Winston — 2.7); kontekst 2.6-bob
Sirlarni redact (parol/token — 14, 2.7); to'g'ri daraja 2.8-bob
So'rov interceptor (vaqt — 2.11)
ERROR:
Global exception filter (markaziy — 2.9); toza javob 5.10-bob
To'g'ri HTTP status 2.10-bob; custom exception (domen)
Xato log (stack — 5.12); ilova yiqilmaydi
Health check (monitoring — 2.13)3. Sintaksis — tez ma'lumotnoma
// Cache (2.1, 2.4)
CacheModule.registerAsync({ useFactory: () => ({ store: await redisStore({...}) }) })
@UseInterceptors(CacheInterceptor) @CacheKey("x") @CacheTTL(300000)
@Inject(CACHE_MANAGER) cache: Cache cache.get/set/del
// Logger (2.6, 2.7)
private logger = new Logger(Context.name) logger.log/error/warn/debug
class MyLogger implements LoggerService { log() {} error() {} } // custom (2.6-b)
app.useLogger(new MyLogger()) // yoki Pino/Winston adapter
LoggerModule.forRoot({ pinoHttp: { redact: [...] } }) // Pino structured
// Error (2.9, 2.10)
@Catch() class Filter implements ExceptionFilter { catch(ex, host) {} }
constructor(private httpAdapterHost: HttpAdapterHost) {} // platforma-agnostik (2.9-b)
throw new NotFoundException() / BadRequestException() / ...4. Batafsil kod namunalari
Misol 1 — Redis cache setup (2.2)
// app.module.ts
import { CacheModule } from "@nestjs/cache-manager";
import { redisStore } from "cache-manager-redis-yet";
@Module({
imports: [
CacheModule.registerAsync({
isGlobal: true,
inject: [ConfigService],
useFactory: async (config: ConfigService) => ({
store: await redisStore({
socket: {
host: config.get("REDIS_HOST"),
port: config.get<number>("REDIS_PORT"),
},
ttl: 60000,
}),
}),
}),
],
})
export class AppModule {}Misol 2 — Cache-aside service (2.4)
@Injectable()
export class ProductsService {
constructor(
@InjectRepository(Product) private repo: Repository<Product>,
@Inject(CACHE_MANAGER) private cache: Cache,
) {}
async bitta(id: number): Promise<Product> {
const kalit = `product:${id}`;
const keshda = await this.cache.get<Product>(kalit);
if (keshda) return keshda; // cache hit (tez — 2.12)
const mahsulot = await this.repo.findOneBy({ id });
if (!mahsulot) throw new NotFoundException("Topilmadi");
await this.cache.set(kalit, mahsulot, 300000); // 5 daqiqa
return mahsulot;
}
async hammasi(): Promise<Product[]> {
const keshda = await this.cache.get<Product[]>("products:all");
if (keshda) return keshda;
const mahsulotlar = await this.repo.find();
await this.cache.set("products:all", mahsulotlar, 60000);
return mahsulotlar;
}
async yangila(id: number, dto: UpdateProductDto) {
const natija = await this.repo.update(id, dto);
// Invalidatsiya (2.5 — eng muhim)
await this.cache.del(`product:${id}`);
await this.cache.del("products:all");
return natija;
}
async ochir(id: number) {
await this.repo.delete(id);
await this.cache.del(`product:${id}`);
await this.cache.del("products:all");
}
}Misol 3 — CacheInterceptor (avtomatik — 2.3)
@Controller("products")
export class ProductsController {
@Get()
@UseInterceptors(CacheInterceptor) // avtomatik kesh
@CacheKey("products_all")
@CacheTTL(60000)
hammasi() {
return this.service.hammasi();
}
// Mutatsiya — kesh yo'q (interceptor faqat GET)
@Post()
yarat(@Body() dto: CreateProductDto) {
return this.service.yarat(dto); // service'da invalidatsiya (Misol 2)
}
}Misol 4 — Pino logger setup (2.7)
// app.module.ts
import { LoggerModule } from "nestjs-pino";
@Module({
imports: [
LoggerModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
pinoHttp: {
level: config.get("app.env") === "production" ? "info" : "debug",
transport: config.get("app.env") !== "production"
? { target: "pino-pretty", options: { colorize: true } } // dev
: undefined, // prod: JSON
redact: { // sirlarni yashirish (14)
paths: ["req.headers.authorization", "req.body.parol", "req.body.token"],
censor: "***",
},
customProps: (req) => ({ userId: (req as any).user?.id }), // har log'da user
},
}),
}),
],
})
export class AppModule {}
// main.ts
const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.useLogger(app.get(Logger)); // Pino'ni NestJS logger qilishMisol 5 — Global exception filter (to'liq — 2.9)
// filters/global-exception.filter.ts
import { Catch, ExceptionFilter, ArgumentsHost, HttpException, HttpStatus, Logger } from "@nestjs/common";
@Catch()
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 = "Server xatosi yuz berdi";
if (exception instanceof HttpException) {
status = exception.getStatus();
message = exception.getResponse();
}
// Log 5.12-bob — server xato (500) batafsil
if (status >= 500) {
this.logger.error(
`${request.method} ${request.url} ${status}`,
(exception as Error).stack,
);
} else {
this.logger.warn(`${request.method} ${request.url} ${status}: ${JSON.stringify(message)}`);
}
// Toza javob (5.10 — ichki tafsilot oshkor qilinmaydi — 14)
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: typeof message === "string" ? message : (message as any).message,
});
}
}
// main.ts (global)
app.useGlobalFilters(new GlobalExceptionFilter());
// yoki APP_FILTER (DI bilan — 8.6: 2.12)Misol 6 — Logging interceptor (so'rov vaqti — 2.11)
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private logger = new Logger("HTTP");
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const req = context.switchToHttp().getRequest();
const { method, url } = req;
const boshlanish = Date.now();
return next.handle().pipe(
tap(() => {
const vaqt = Date.now() - boshlanish;
const status = context.switchToHttp().getResponse().statusCode;
const daraja = vaqt > 1000 ? "warn" : "log"; // sekin warn
this.logger[daraja](`${method} ${url} ${status} — ${vaqt}ms`);
}),
catchError((err) => {
const vaqt = Date.now() - boshlanish;
this.logger.error(`${method} ${url} — ${vaqt}ms — ${err.message}`);
throw err;
}),
);
}
}Misol 7 — Custom exception'lar (domen — 2.10)
// exceptions/insufficient-stock.exception.ts
export class InsufficientStockException extends HttpException {
constructor(mahsulot: string, mavjud: number, kerak: number) {
super(
{
statusCode: HttpStatus.BAD_REQUEST,
error: "InsufficientStock",
message: `${mahsulot}: faqat ${mavjud} ta mavjud (${kerak} ta so'raldi)`,
},
HttpStatus.BAD_REQUEST,
);
}
}
// Ishlatish (service)
if (mahsulot.zaxira < kerak) {
throw new InsufficientStockException(mahsulot.nom, mahsulot.zaxira, kerak);
}Misol 8 — Cache decorator (qayta ishlatish — kengaytma)
// Cacheable custom decorator (DRY)
export function Cacheable(kalitPrefix: string, ttl = 300000) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = async function (...args: any[]) {
const cache: Cache = this.cache;
const kalit = `${kalitPrefix}:${JSON.stringify(args)}`;
const keshda = await cache.get(kalit);
if (keshda) return keshda;
const natija = await original.apply(this, args);
await cache.set(kalit, natija, ttl);
return natija;
};
};
}
// @Cacheable("user") async bitta(id) {...}Misol 9 — Redis to'g'ridan (cache emas — sessiya/navbat)
// Redis'ni to'g'ridan ishlatish (cache'dan tashqari — session, rate limit, pub/sub)
import { InjectRedis } from "@nestjs-modules/ioredis";
import Redis from "ioredis";
@Injectable()
export class RateLimitService {
constructor(@InjectRedis() private redis: Redis) {}
async tekshir(userId: string): Promise<boolean> {
const kalit = `ratelimit:${userId}`;
const soni = await this.redis.incr(kalit); // atomik oshirish
if (soni === 1) await this.redis.expire(kalit, 60); // 60 soniya oyna
return soni <= 10; // 1 daqiqada 10
}
}
// Redis — cache + session (8.12 bot) + rate limit + pub/sub (5.13)Misol 10 — To'liq production setup (2.14)
// main.ts — production observability
async function bootstrap() {
const app = await NestFactory.create(AppModule, { bufferLogs: true });
const config = app.get(ConfigService);
app.useLogger(app.get(Logger)); // Pino (Misol 4)
app.useGlobalFilters(new GlobalExceptionFilter()); // markaziy xato (Misol 5)
app.useGlobalInterceptors(new LoggingInterceptor()); // so'rov log (Misol 6)
app.useGlobalPipes(new ValidationPipe({ whitelist: true })); // (8.5)
app.enableShutdownHooks(); // graceful shutdown (8.1)
await app.listen(config.get("app.port"));
}
bootstrap();
// cache (tez) + log (ko'rinish) + error filter (barqaror) + health = production5. To'g'ri va noto'g'ri holatlar
1) Cache invalidatsiyasiz
o'zgargandan keyin eski cache (eski ma'lumot — 2.5)
update/delete'da cache.del2) Sirlarni loglash
parol/token log'da (14)
redact (Pino — 2.7)3) Xato ushlanmaydi (ilova yiqiladi)
filter yo'q (xato 500 + stack oshkor — 5.10)
global exception filter (toza javob + log — 2.9)4) Hammani keshlash
tez o'zgaradigan/maxfiy kesh (eski/xavf — 2.12)
kam o'zgaradi + ko'p o'qiladi5) console.log production'da
console.log (structured emas, sekin — 5.12)
Logger / Pino (JSON, daraja)6. Keng tarqalgan xatolar va yechimlari
Xato 1 — Cache ishlamaydi (har safar DB)
Sababi: kalit har xil, yoki TTL 0 2.4-bob. Yechimi: kalit izchil; TTL to'g'ri.
Xato 2 — Eski ma'lumot (cache stale)
Sababi: invalidatsiya yo'q 2.5-bob. Yechimi: o'zgarganda del.
Xato 3 — Redis ulanmaydi
Sababi: host/port noto'g'ri, Redis ishlamayapti 5.21-bob. Yechimi: Redis ishga tushir; config; Docker.
Xato 4 — Log production'da ko'rinmaydi
Sababi: daraja noto'g'ri, yoki useLogger yo'q 2.7-bob. Yechimi: level: info; app.useLogger.
Xato 5 — Stack trace foydalanuvchiga ketadi
Sababi: exception filter yo'q (2.9, 14). Yechimi: global filter (toza javob).
Xato 6 — CacheInterceptor mutatsiyani keshlaydi
Sababi: POST'ga interceptor 2.3-bob. Yechimi: faqat GET'ga.
7. Integratsiya — bu mavzu stack'ning qayerida uchraydi
- Caching 5.21-bob: Redis — CacheModule.
- Logger 5.12-bob: Pino/Winston.
- Error handling 5.10-bob: exception filter.
- Interceptor/Filter 8.6-bob: cache, logging, exception.
- Redis 8.12-bob: session, rate limit, pub/sub.
- Config 8.14-bob: Redis/log sozlama.
- DevOps (10): monitoring, health, masshtab.
- Throttler 8.16-bob: rate limit (Redis).
8. Eng yaxshi amaliyotlar (best practices)
- Redis cache (taqsimlangan — prod — 2.2); to'g'ri narsani keshlash 2.12-bob.
- Cache invalidatsiya (o'zgarganda del — 2.5); TTL.
- Structured JSON log (Pino/Winston — prod — 2.7); kontekst 2.6-bob.
- Sirlarni redact (parol/token — 14, 2.7); to'g'ri daraja 2.8-bob.
- So'rov interceptor (vaqt — sekin topish — 2.11).
- Global exception filter (markaziy, toza javob — 2.9).
- To'g'ri HTTP status (built-in + custom — 2.10).
- Xato log (stack); ilova yiqilmaydi 2.9-bob.
- Health check (monitoring — 2.13); graceful shutdown.
- console.log emas (Logger — 5.12).
9. Amaliy loyiha: "Production-Tayyor Backend (Cache + Log + Error)"
NestJS production poydevorini mustahkamlash.
Maqsad
Ilovaga cache (Redis), structured log (Pino), markaziy error handling qo'shib, production-tayyor qilish.
Talablar (requirements)
- Redis cache: CacheModule.registerAsync (Misol 1, 2.2).
- Cache-aside: get/set/del + invalidatsiya (Misol 2, 2.4, 2.5).
- CacheInterceptor: GET avtomatik kesh (Misol 3, 2.3).
- Pino logger: structured, redact, muhitga bog'liq (Misol 4, 2.7).
- Logging interceptor: so'rov + vaqt (Misol 6, 2.11).
- Global exception filter: markaziy, toza javob, log (Misol 5, 2.9).
- Custom exception: domen xatosi (Misol 7, 2.10).
- Redis to'g'ridan: rate limit (Misol 9).
- Health check: DB/Redis 2.13-bob.
- Production setup: main.ts (cache+log+filter+graceful — Misol 10, 2.14).
Maslahatlar (hint)
- Cache invalidatsiya (del — 2.5, 2-xato).
- Redact sirlar (14, 2-holat).
- Exception filter toza javob (2.9, 5-xato).
- To'g'ri narsani kesh (2.12, 4-holat).
- CacheInterceptor faqat GET (2.3, 6-xato).
- Logger (console.log emas — 5.12, 5-holat).
"Tayyor" mezonlari (acceptance criteria)
- Redis cache.
- Cache-aside + invalidatsiya.
- CacheInterceptor.
- Pino logger (redact).
- Logging interceptor.
- Global exception filter.
- Custom exception.
- Redis rate limit.
- Health check.
- Production main.ts.
Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.
10. Xulosa va keyingi bobga ko'prik
Bu bobda production poydevorining uch ustunini chuqur o'rgandik:
- Caching: CacheModule (in-memory Redis — 2.1, 2.2); CacheInterceptor (avtomatik — 2.3); qo'lda cache-aside + invalidatsiya (2.4, 2.5); tezlik 2.12-bob.
- Logger: NestJS Logger 2.6-bob; Pino/Winston (structured JSON, redact — 2.7); darajalar 2.8-bob; logging interceptor 2.11-bob.
- Error handling: global exception filter (markaziy — 2.9); xato turlari + status 2.10-bob; custom exception; health check 2.13-bob.
Keyingi bob — 8.16-bob: Microservices (NestJS mikroservislar). Production poydevorini bildik; endi NestJS'ning ilg'or arxitektura mavzusiga — mikroservislar ga — o'tamiz: monolit vs mikroservis, transport (TCP/Redis/RabbitMQ/Kafka), message/event pattern, API Gateway. Bu — katta, masshtablanadigan tizimlar arxitekturasi (9, 10 bilan bog'liq).
Foydalanilgan rasmiy/ishonchli manbalar
- docs.nestjs.com/techniques/caching (CacheModule, CacheInterceptor, Redis store)
- docs.nestjs.com/techniques/logger (built-in Logger, LoggerService, useLogger)
- github.com/iamolegga/nestjs-pino (structured logging, redact, request context)
- github.com/gremo/nest-winston (Winston integratsiya, transportlar)
- docs.nestjs.com/exception-filters (global exception filter, HttpException, HttpAdapterHost)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!