Exception Filters va Error Handling
Xato nima va nima uchun muhim?
Har qanday dasturda xato bo'ladi. Foydalanuvchi mavjud bo'lmagan sahifaga kiradi, noto'g'ri parol kiritadi, server bilan bog'lanish uziladi. Bular — kutilgan xatolar. Dasturchi vazifasi bu xatolarni to'g'ri ushlash va foydalanuvchiga tushunarli xabar berish.
Tasavvur qiling: siz do'konga kirasiz, sotuvchidan mahsulot so'raysiz. U jimgina orqasini o'girib ketsa — bu yomon. Lekin "kechirasiz, bu mahsulot tugagan, o'rniga buni tavsiya qilaman" desa — bu professional.
NestJS da xatolarni boshqarish ham xuddi shunday. Foydalanuvchiga har doim aniq, tushunarli javob berish kerak.
HTTP Exceptionlar — NestJS ning tayyor xatolari
NestJS HttpException sinfi va undan meros olgan ko'plab tayyor xato sinflari beradi. Har birining o'z status kodi bor.
import {
NotFoundException,
BadRequestException,
UnauthorizedException,
ForbiddenException,
ConflictException,
InternalServerErrorException,
} from "@nestjs/common";
| Sinf | Status kod | Qachon ishlatiladi |
|---|---|---|
BadRequestException |
400 | Noto'g'ri ma'lumot kelsa |
UnauthorizedException |
401 | Login qilinmagan bo'lsa |
ForbiddenException |
403 | Ruxsat yo'q bo'lsa |
NotFoundException |
404 | Topilmasa |
ConflictException |
409 | Allaqachon mavjud bo'lsa |
InternalServerErrorException |
500 | Server xatosi |
Keling, bularni amalda ko'ramiz:
// users/users.service.ts
import {
Injectable,
NotFoundException,
ConflictException,
} from "@nestjs/common";
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: "Jasur", email: "jasur@example.com" },
{ id: 2, name: "Malika", email: "malika@example.com" },
];
findOne(id: number) {
const user = this.users.find((u) => u.id === id);
// Topilmasa — 404 xatosi
if (!user) {
throw new NotFoundException(`${id}-foydalanuvchi topilmadi`);
}
return user;
}
create(data: { name: string; email: string }) {
const exists = this.users.find((u) => u.email === data.email);
// Allaqachon mavjud bo'lsa — 409 xatosi
if (exists) {
throw new ConflictException("Bu email allaqachon ro'yxatdan o'tgan");
}
const newUser = { id: this.users.length + 1, ...data };
this.users.push(newUser);
return newUser;
}
}
throw — xatoni "otib yuboradi". NestJS uni ushlaydi va foydalanuvchiga avtomatik javob qaytaradi:
{
"statusCode": 404,
"message": "1-foydalanuvchi topilmadi",
"error": "Not Found"
}
Siz bu JSON ni yozmadingiz — NestJS o'zi shakllantirdi.
HttpException — asosiy sinf
Tayyor sinflarda kerakli variant yo'q bo'lsa — HttpException dan to'g'ridan-to'g'ri foydalanasiz:
import { HttpException, HttpStatus } from "@nestjs/common";
throw new HttpException("Maxsus xato xabari", HttpStatus.PAYMENT_REQUIRED);
// 402 status kodi qaytadi
Yoki javob tuzilishini o'zingiz belgilaysiz:
throw new HttpException(
{
statusCode: 400,
message: "Noto'g'ri so'rov",
error: "Validation Error",
details: ["Ism bo'sh bo'lmasligi kerak", "Email noto'g'ri"],
},
HttpStatus.BAD_REQUEST,
);
Bu holda foydalanuvchi quyidagini ko'radi:
{
"statusCode": 400,
"message": "Noto'g'ri so'rov",
"error": "Validation Error",
"details": ["Ism bo'sh bo'lmasligi kerak", "Email noto'g'ri"]
}
Exception Filter nima va nima uchun kerak?
Tayyor xato sinflari ko'p narsani hal qiladi. Lekin real loyihalarda muammolar bo'ladi:
- Barcha xatolarda javob formati har xil ko'rinadi
- Xatolarni logga yozish kerak
- Ba'zi xatolarni alohida ushlash kerak — masalan, ma'lumotlar bazasi xatolari
Aynan shu holatlar uchun Exception Filter yoziladi. Bu — barcha xatolarni markaziy bir joyda ushlash va bir xil formatda qaytarish imkonini beradi.
Keling, global Exception Filter yozamiz:
// common/filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from "@nestjs/common";
import { Request, Response } from "express";
@Catch(HttpException) // faqat HttpException turini ushlaydi
export class HttpExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(HttpExceptionFilter.name);
catch(exception: HttpException, host: ArgumentsHost) {
// HTTP kontekstini olamiz
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
// Xato status kodini olamiz
const status = exception.getStatus();
// Xato javobini olamiz
const exceptionResponse = exception.getResponse();
// Xabarni aniqlaymiz — string yoki object bo'lishi mumkin
const message =
typeof exceptionResponse === "string"
? exceptionResponse
: (exceptionResponse as any).message;
// Loglash — server konsolida ko'rinadi
this.logger.error(
`${request.method} ${request.url} — ${status} — ${JSON.stringify(message)}`,
);
// Bir xil formatda javob qaytaramiz
response.status(status).json({
success: false,
statusCode: status,
message,
path: request.url,
timestamp: new Date().toISOString(),
});
}
}
Endi bu filterni tushuntirib o'tamiz:
@Catch(HttpException) — bu filter faqat HttpException va undan meros olgan sinflarni ushlaydi. NotFoundException, BadRequestException hammasi HttpException dan meros oladi — demak barchasi ushlanadi.
host.switchToHttp() — NestJS turli protokollarni qo'llab-quvvatlaydi: HTTP, WebSocket, gRPC. Bu qator "men HTTP bilan ishlamoqchiman" deydi va request, response ob'ektlarini beradi.
exception.getResponse() — xato ob'ektining ichidagi ma'lumotni oladi. Bu string ham, object ham bo'lishi mumkin. Shuning uchun tekshiramiz.
this.logger.error(...) — xatoni server konsolga yozadi. Keyinchalik xatolarni kuzatish uchun muhim.
Global Filter ni ulash
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { HttpExceptionFilter } from "./common/filters/http-exception.filter";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();
Endi ilovada istalgan joyda xato otilsa — bu filter ushlaydi va bir xil formatda qaytaradi:
{
"success": false,
"statusCode": 404,
"message": "1-foydalanuvchi topilmadi",
"path": "/users/1",
"timestamp": "2024-01-15T10:30:00.000Z"
}
Har bir endpoint da alohida yozmasdan — bir martalik sozlama.
Barcha xatolarni ushlash
@Catch(HttpException) faqat HTTP xatolarni ushlaydi. Lekin ba'zan kutilmagan xatolar ham bo'ladi — masalan, ma'lumotlar bazasi to'satdan ishlamay qoldi, yoki kodda undefined ga murojaat qilindi.
Buning uchun @Catch() — hech narsa ko'rsatilmasa, barcha xatolarni ushlaydi:
// common/filters/all-exceptions.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from "@nestjs/common";
import { Request, Response } from "express";
@Catch() // barcha xatolarni ushlaydi
export class AllExceptionsFilter implements ExceptionFilter {
private readonly logger = new Logger(AllExceptionsFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
// HttpException bo'lsa — uning kodini olamiz
// Boshqa xato bo'lsa — 500 (server xatosi)
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException
? exception.message
: "Ichki server xatosi";
// Kutilmagan xatolarni batafsil loglaymiz
if (!(exception instanceof HttpException)) {
this.logger.error("Kutilmagan xato:", exception);
}
response.status(status).json({
success: false,
statusCode: status,
message,
path: request.url,
timestamp: new Date().toISOString(),
});
}
}
Bu filter ikki holatni farqlaydi:
Agar xato HttpException bo'lsa — uning status kodi va xabarini ishlatadi. Bu biz ataylab otgan xatolar — NotFoundException, BadRequestException va h.k.
Agar xato boshqa narsа bo'lsa — masalan, TypeError, ReferenceError — bu kutilmagan xato. Status kod 500, xabar "Ichki server xatosi". Foydalanuvchi texnik tafsilotlarni ko'rmaydi, lekin server konsolda to'liq xato yoziladi.
Filter ni qayerga qo'llash mumkin
Xuddi Pipe kabi, Filter ham uch joyda ishlashi mumkin.
Global — butun ilova uchun, main.ts da:
app.useGlobalFilters(new AllExceptionsFilter());
Controller darajasida — faqat shu controllerdagi barcha metodlar uchun:
import { Controller, UseFilters } from "@nestjs/common";
import { HttpExceptionFilter } from "../common/filters/http-exception.filter";
@Controller("users")
@UseFilters(HttpExceptionFilter)
export class UsersController {}
Metod darajasida — faqat shu metod uchun:
import { Get, Param, UseFilters } from "@nestjs/common";
import { ParseIntPipe } from "@nestjs/common";
@Get(':id')
@UseFilters(HttpExceptionFilter)
findOne(@Param('id', ParseIntPipe) id: number) {}
Real loyihalarda global filter qo'yish eng to'g'ri yondashuv. Barcha xatolar bir joydan o'tadi, bir xil formatda qaytadi.
O'zingizning Exception sinfi
Loyiha o'sgani sari maxsus xato sinflari kerak bo'ladi. Masalan, "to'lov amalga oshmadi", "limit tugadi" kabi holatlar uchun:
// common/exceptions/payment-failed.exception.ts
import { HttpException, HttpStatus } from "@nestjs/common";
export class PaymentFailedException extends HttpException {
constructor(reason: string) {
super(
{
statusCode: HttpStatus.PAYMENT_REQUIRED,
message: `To'lov amalga oshmadi: ${reason}`,
error: "Payment Failed",
},
HttpStatus.PAYMENT_REQUIRED,
);
}
}
Ishlatish:
throw new PaymentFailedException("Karta balansi yetarli emas");
Javob:
{
"statusCode": 402,
"message": "To'lov amalga oshmadi: Karta balansi yetarli emas",
"error": "Payment Failed"
}
Har safar HttpException ni sozlash o'rniga — bir marta yozdingiz, hamma joyda ishlatdingiz.
Xulosa
Xatolarni to'g'ri boshqarish — professional dasturchi belgisi. Foydalanuvchi hech qachon "500 Internal Server Error" yoki bo'sh ekran ko'rmasligi kerak. Har doim tushunarli xabar, to'g'ri status kod.
Eslab qoling:
NotFoundException,BadRequestExceptionva boshqalar — eng ko'p ishlatiladigan tayyor xatolarthrow— xatoni otadi, NestJS ushlaydi@Catch(HttpException)— faqat HTTP xatolarni ushlaydi@Catch()— barcha xatolarni ushlaydi- Global filter —
main.tsda bir marta, hamma joyda ishlaydi - O'z Exception sinfi —
HttpExceptiondan meros oling
Keyingi dars
8-dars: Middleware