WisarWisar
Dasturlash kitobi/5-QISM — Nodejs18 daqiqa

5.10-bob: Error handling middleware, global error handler

5-QISM — Node.js Backend · 10-mavzu


1. Kirish va motivatsiya

2.12-bobda JavaScript'da xatolarni (try/catch, custom error class) o'rgandik. 5.6-bobda Express error middleware'ni (4 argument), 5.9-bobda validatsiya xatolarini ko'rdik. Endi hammasini birlashtirib, butun ilovadagi xatolarni bir joyda, izchil, professional boshqarishni — global error handlerni — chuqur o'rganamiz.

Xato handling — havaskor va professional backend'ni eng aniq ajratadigan narsa. Yangi dasturchi har route'da try/catch yozadi, xato xabarlarini har xil qaytaradi, ba'zida xatoni umuman e'tiborsiz qoldiradi (server qulaydi). Professional — markazlashtirilgan error handling quradi: har route xatoni faqat tashlaydi (throw), bitta global handler uni ushlab, izchil javob qaytaradi, log qiladi, va serverni qulashdan saqlaydi.

O'xshatish: error handling — binoning yong'in xavfsizligi tizimi. Har xonada alohida o'choq emas (har route'da try/catch) — balki markaziy tizim: har joydagi tutun (xato) datchikka (next(err)) boradi, markaziy panel (global handler) javob beradi (signal, suv), hodisani qayd qiladi (log). Markazlashgan, izchil, ishonchli.

Nega muhim?

  • Barqarorlik — xato serverni qulatmaydi (boshqa foydalanuvchilar ishlaydi).
  • Izchillik — har xato bir xil formatda 5.7-bob — frontend 11.10-bob bashorat qiladi.
  • Xavfsizlik (14) — texnik tafsilot (DB struktura, stack) foydalanuvchiga oshkor qilinmaydi.
  • Debug/monitoring — xatolar bir joyda log qilinadi (5.12: Winston, Sentry — 10.9).

2. Nazariya — chuqur tushuntirish

2.1. Markazlashtirilgan error handling falsafasi

Asosiy g'oya: route'lar xatoni hal qilmaydi, balki tashlaydi (throw yoki next(err)); bitta global handler hammasini ushlaydi (expressjs.com):

text
   Har route'da:                 Markazlashgan:
  try { ... } catch (e) {         route: throw new AppError(...)
    res.status(500).json(...)            │ next(err)
  }                                      ▼
  (har joyda takror, har xil)     [GLOBAL ERROR HANDLER]
                                   (bir joyda: log + izchil javob)

Foyda: route'lar toza (faqat biznes logika); xato javobi bir joyda (izchil — 5.7); o'zgartirish oson (bir joyni).

2.2. Operational vs Programmer xatolar (muhim farq)

Xatolar ikki turga bo'linadi (Toptal, best practices):

  • Operational (kutilgan) xatolar — ilovaning normal qismi: noto'g'ri kirish 5.9-bob, topilmadi (404), ruxsat yo'q (401/403), tarmoq uzilishi. Bularni boshqaramiz (foydalanuvchiga aniq javob).
  • Programmer (bug) xatolarkutilmagan kod xatolari: undefinedga murojaat (2.1-JS), typo, noto'g'ri mantiq. Bular — bug; ularni tuzatish kerak, "boshqarib" yashirmaslik.
text
  Operational:  "Email band" (409), "Topilmadi" (404)  foydalanuvchiga javob
  Programmer:   "Cannot read properties of undefined"  log + (kerak bo'lsa) crash + restart

Nega farq muhim: operational xatoda — foydalanuvchiga chiroyli javob (4xx). Programmer xatoda — bu bug; uni yashirmasdan log qiling, kuzating (5.12, 10.9), tuzating. Jiddiy bug'da — graceful crash + restart (PM2 — 10.7), chunki ilova "noto'g'ri holatda" bo'lishi mumkin 2.10-bob.

2.3. Custom error class iyerarxiyasi (2.12-JS chuqur)

Markazlashgan handling uchun custom error class (2.12-JS, 2.10-JS: extends) — xatoga status, kod, operational flag biriktiradi:

js
// Asosiy operational error
class AppError extends Error {
  constructor(message, statusCode = 500) {
    super(message);
    this.statusCode = statusCode;       // HTTP status (5.7)
    this.isOperational = true;          // kutilgan xato 2.2-bob — bug emas
    Error.captureStackTrace(this, this.constructor);   // toza stack (5.12)
  }
}

// Maxsus xatolar (2.10-JS: inheritance)
class NotFoundError extends AppError {
  constructor(resurs = "Resurs") { super(`${resurs} topilmadi`, 404); }
}
class ValidationError extends AppError {
  constructor(message, details) { super(message, 400); this.details = details; }
}
class UnauthorizedError extends AppError {
  constructor() { super("Avtorizatsiya kerak", 401); }   // (5.7: 401)
}
class ForbiddenError extends AppError {
  constructor() { super("Ruxsat yo'q", 403); }           // (5.7: 403)
}

isOperational flag — handler'ga "bu kutilgan xatomi (foydalanuvchiga javob) yoki bug (log + ehtiyot)?" deyishga yordam beradi 2.2-bob.

2.4. Global error handler middleware (4 argument!)

Global handler — 4 argumentli middleware (5.6: shuning uchun Express uni error handler deb biladi), eng oxirda (expressjs.com):

js
const errorHandler = (err, req, res, next) => {     // 4 ARGUMENT (5.6)
  // Default qiymatlar
  const statusCode = err.statusCode || 500;          // (2.3)
  const isOperational = err.isOperational || false;

  // Log 5.12-bob — programmer xato bo'lsa to'liq
  if (!isOperational) console.error("BUG:", err.stack);

  // Izchil javob 5.7-bob — prod'da texnik tafsilot YASHIRIN (14)
  res.status(statusCode).json({
    success: false,
    error: {
      message: isOperational ? err.message : "Ichki server xatosi",   // bug'da yashir (14)
      ...(err.details && { details: err.details }),                     // validatsiya (5.9)
      ...(process.env.NODE_ENV !== "production" && { stack: err.stack }),// dev'da stack (5.8)
    },
  });
};

// ENG OXIRDA (barcha route'lardan keyin — 5.6)
app.use(errorHandler);

2.5. next(err) — xatoni handler'ga uzatish

Route'da xato bo'lsa, uni next(err) bilan global handler'ga uzatish (5.6, expressjs.com):

js
app.get("/users/:id", (req, res, next) => {
  const user = users.find(u => u.id === Number(req.params.id));
  if (!user) {
    return next(new NotFoundError("Foydalanuvchi"));   // handler'ga uzat (2.4)
  }
  res.json(user);
});

throw vs next(err): sinxron kodda throw ham ishlaydi (Express ushlaydi). Asinxron (2.11-JS) kodda — next(err) (yoki Express 5 / wrapper — 2.6). Ikkalasi ham global handler'ga boradi.

2.6. Async xato handling (eng muhim nuans)

Express 4 async (2.11-JS) xatolarni avtomatik ushlamaydi (5.6: shuni ko'rgandik). Async handler'da throw qilsangiz — Express bilmaydi, so'rov "osiladi" yoki process qulaydi:

js
//  async xato ushlanmaydi (Express 4)
app.get("/x", async (req, res) => {
  const data = await DB.query();      // xato  ushlanmaydi!
  res.json(data);
});

//  Yechim 1: try/catch + next(err)
app.get("/x", async (req, res, next) => {
  try {
    res.json(await DB.query());
  } catch (err) {
    next(err);                         // handler'ga (2.5)
  }
});

//  Yechim 2: async wrapper (DRY — eng yaxshi)
const catchAsync = (fn) => (req, res, next) => fn(req, res, next).catch(next);
app.get("/x", catchAsync(async (req, res) => {
  res.json(await DB.query());          // xato avtomatik next(err)ga
}));

//  Yechim 3: express-async-errors paketi (import qilsa, avtomatik)
// import "express-async-errors";

//  Express 5: avtomatik (next(err) shart emas — best practices)

Tavsiya: async wrapper (catchAsync) — eng keng tarqalgan, toza yechim (Express 4'da). Yoki express-async-errors paketi. Express 5'da — avtomatik (kelajak). NestJS 8.6-bob buni butunlay hal qiladi.

2.7. 404 handler (route topilmadi)

Hech bir route mos kelmasa — 404 (5.5, 5.6). Buni route'lardan keyin, error handler'dan oldin middleware bilan:

js
// Barcha route'lardan KEYIN
app.use((req, res, next) => {
  next(new NotFoundError(`Yo'l: ${req.originalUrl}`));   // handler'ga (2.4, 2.5)
});
// keyin error handler (2.4)
app.use(errorHandler);

2.8. Error handler tartibi (joylashuv muhim)

Error handler majburiy oxirda (5.6: tartib):

text
  app.use(express.json())         1. middleware'lar
  app.use("/api", routes)         2. route'lar
  app.use(404 handler)            3. 404 (route topilmadi)
  app.use(errorHandler)           4. ERROR HANDLER (eng oxirgi!)

Agar error handler route'lardan oldin bo'lsa — u hech qachon ishlamaydi (so'rov route'ga yetmaydi). Doim oxirda.

2.9. Bir nechta error handler

Murakkab ilovada bir nechta error handler bo'lishi mumkin (har biri next(err) bilan keyingisiga uzatadi — 5.6):

js
// 1. Maxsus xatolar (DB, validatsiya)
app.use((err, req, res, next) => {
  if (err.name === "ZodError") return res.status(400).json({...});   // (5.9)
  if (err.code === "11000") return res.status(409).json({...});      // MongoDB duplicate (6)
  next(err);                          // boshqasini keyingiga uzat
});
// 2. Umumiy fallback handler
app.use(errorHandler);

2.10. Texnik tafsilotni yashirish (xavfsizlik — 14)

Production'da xato javobida texnik tafsilot (stack, DB xato, fayl yo'li) oshkor qilinmaydi (14): hacker'ga ma'lumot beradi:

text
   Production'da:  { error: "ECONNREFUSED 127.0.0.1:5432 ...", stack: "..." }
                    (DB manzili, kod tuzilishi oshkor — 14)
   Production'da:  { error: "Ichki server xatosi" }  (umumiy)
     Dev'da:        + stack (debug uchun — 5.8: NODE_ENV)
     Har holatda:   to'liq xato LOG'da 5.12-bob — kuzatish uchun

2.11. Xatolarni log qilish (5.12 ga ko'prik)

Global handler — xatolarni markaziy log qilish joyi (best practices). console.error — boshlash uchun; production'da Winston/Pino 5.12-bob, va Sentry 10.9-bob kabi monitoring:

js
const errorHandler = (err, req, res, next) => {
  logger.error({                       // Winston (5.12)
    message: err.message,
    stack: err.stack,
    url: req.originalUrl,              // qaysi so'rov
    method: req.method,
    user: req.user?.id,               // kim (5.15)
  });
  // ... javob ...
};

2.12. Process-level xatolar (uncaughtException, unhandledRejection)

Express handler faqat so'rov ichidagi xatolarni ushlaydi. So'rovdan tashqari (yoki ushlanmagan async — 2.11-JS) xatolar — process darajasida (5.1: process):

js
// Ushlanmagan Promise rad etilishi (2.11-JS)
process.on("unhandledRejection", (reason) => {
  logger.error("Unhandled Rejection:", reason);   // log (5.12)
  // graceful shutdown (2.13)
});

// Ushlanmagan sinxron xato (programmer error — 2.2)
process.on("uncaughtException", (err) => {
  logger.error("Uncaught Exception:", err);
  process.exit(1);                     // CRASH (bug — holat ishonchsiz, 2.2)
  // PM2 10.7-bob qayta ishga tushiradi
});

uncaughtException'dan keyin process'ni to'xtating (process.exit(1)) — bu programmer error (bug — 2.2); ilova "noto'g'ri holatda" bo'lishi mumkin. Log qilib, graceful crash + restart (PM2 — 10.7) eng xavfsiz (best practices). Davom etish — yashirin buzilishlarga olib keladi.

2.13. Graceful shutdown (xato/signal'da)

Server o'chayotganda (deploy — 10, fatal xato) — yangi so'rovni qabul qilmay, joriy so'rovlarni tugatib yopilish (5.5: graceful shutdown):

js
const server = app.listen(3000);

process.on("SIGTERM", () => {          // (5.5, 0.3: signal)
  logger.info("SIGTERM: server yopilmoqda");
  server.close(() => {                  // joriy so'rovlarni tugatib yop
    // DB ulanishlarni yop (6), Redis 5.21-bob...
    process.exit(0);
  });
});

2.14. Validatsiya va DB xatolarini moslash (5.9, 6)

Tashqi kutubxona xatolarini (Zod, Mongoose) o'z formatingga moslash — error handler'da yoki maxsus handler'da 2.9-bob:

js
const errorHandler = (err, req, res, next) => {
  // Zod validatsiya xatosi (5.9)
  if (err.name === "ZodError") {
    return res.status(400).json({ success: false, error: { code: "VALIDATION", details: err.flatten().fieldErrors } });
  }
  // Mongoose noto'g'ri ID (6)
  if (err.name === "CastError") {
    return res.status(400).json({ success: false, error: { message: "Noto'g'ri ID" } });
  }
  // MongoDB duplicate key (6)
  if (err.code === 11000) {
    return res.status(409).json({ success: false, error: { message: "Allaqachon mavjud" } });   // (5.7: 409)
  }
  // ... umumiy ...
};

2.15. To'liq error handling arxitekturasi (xulosa)

text
  Route handler (catchAsync bilan — 2.6)
     │ throw new NotFoundError(...) yoki next(err)
     ▼
  [Maxsus error handler'lar] (Zod/Mongoose moslash — 2.14)
     │ next(err)
     ▼
  [Global error handler] (izchil javob + log — 2.4, 2.11)
     │
     ▼
  Javob 5.7-bob — operational: aniq; programmer: umumiy (14)

  Tashqari (process — 2.12): uncaughtException/unhandledRejection  log + crash  PM2 restart

3. Sintaksis — tez ma'lumotnoma

js
// Custom error (2.3)
class AppError extends Error {
  constructor(msg, status) { super(msg); this.statusCode = status; this.isOperational = true; }
}

// Async wrapper (2.6)
const catchAsync = (fn) => (req, res, next) => fn(req, res, next).catch(next);

// Route — throw/next (2.5)
if (!user) throw new NotFoundError();   // yoki next(new ...)

// Tartib (2.8)
app.use(routes);
app.use(notFoundHandler);               // 404
app.use(errorHandler);                  // 4 argument, ENG OXIRDA

// Process (2.12)
process.on("unhandledRejection", ...);  process.on("uncaughtException", ...);

4. Batafsil kod namunalari

Misol 1 — Custom error class iyerarxiyasi (2.3)

js
// errors/AppError.js
export class AppError extends Error {
  constructor(message, statusCode = 500) {
    super(message);
    this.statusCode = statusCode;
    this.isOperational = true;            // kutilgan (2.2)
    this.name = this.constructor.name;
    Error.captureStackTrace(this, this.constructor);
  }
}
export class NotFoundError extends AppError {
  constructor(resurs = "Resurs") { super(`${resurs} topilmadi`, 404); }
}
export class BadRequestError extends AppError {
  constructor(message = "Noto'g'ri so'rov") { super(message, 400); }
}
export class UnauthorizedError extends AppError {
  constructor(message = "Avtorizatsiya kerak") { super(message, 401); }
}
export class ForbiddenError extends AppError {
  constructor(message = "Ruxsat yo'q") { super(message, 403); }
}
export class ConflictError extends AppError {
  constructor(message = "Ziddiyat") { super(message, 409); }
}

Misol 2 — Async wrapper (2.6)

js
// utils/catchAsync.js — try/catch takrorlanishini tugatadi (DRY)
export const catchAsync = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);   // xato  next(err) (2.5)
};

Misol 3 — Global error handler (2.4, 2.10, 2.14)

js
// middleware/errorHandler.js
import { config } from "../config/index.js";   // (5.8)
import { logger } from "../utils/logger.js";    // (5.12)

export const errorHandler = (err, req, res, next) => {
  let statusCode = err.statusCode || 500;
  let message = err.message;

  // Tashqi xatolarni moslash (2.14)
  if (err.name === "ZodError") {                 // (5.9)
    statusCode = 400;
    return res.status(400).json({ success: false, error: { code: "VALIDATION", details: err.flatten().fieldErrors } });
  }
  if (err.name === "CastError") { statusCode = 400; message = "Noto'g'ri ID"; }   // (6)
  if (err.code === 11000) { statusCode = 409; message = "Allaqachon mavjud"; }    // (6)

  // Log 2.11-bob — programmer xato to'liq, operational qisqaroq
  if (statusCode >= 500) {
    logger.error({ message: err.message, stack: err.stack, url: req.originalUrl, method: req.method });
  }

  // Javob 2.10-bob — prod'da 5xx tafsilotni yashir (14)
  res.status(statusCode).json({
    success: false,
    error: {
      message: (statusCode >= 500 && config.isProd) ? "Ichki server xatosi" : message,
      ...(!config.isProd && { stack: err.stack }),   // dev'da stack (5.8)
    },
  });
};

Misol 4 — Route'larda ishlatish (2.5, 2.6)

js
import { catchAsync } from "../utils/catchAsync.js";
import { NotFoundError, ConflictError } from "../errors/AppError.js";

// Async — wrapper bilan; xato faqat THROW (try/catch yo'q — toza!)
export const getUser = catchAsync(async (req, res) => {
  const user = await User.findById(req.params.id);   // DB (6)
  if (!user) throw new NotFoundError("Foydalanuvchi");   //  global handler (2.5)
  res.json({ success: true, data: user });
});

export const createUser = catchAsync(async (req, res) => {
  const mavjud = await User.findOne({ email: req.body.email });
  if (mavjud) throw new ConflictError("Email band");     // 409 (2.3)
  const user = await User.create(req.body);
  res.status(201).json({ success: true, data: user });
});
// Route'lar toza — faqat biznes logika; xato markazda boshqariladi (2.1)

Misol 5 — To'liq app.js (tartib — 2.8)

js
import express from "express";
import userRoutes from "./routes/users.js";
import { errorHandler } from "./middleware/errorHandler.js";
import { NotFoundError } from "./errors/AppError.js";

const app = express();
app.use(express.json());                      // 1. middleware (5.6)
app.use("/api/users", userRoutes);            // 2. route'lar

// 3. 404 handler (route topilmadi — 2.7)
app.use((req, res, next) => {
  next(new NotFoundError(`Yo'l: ${req.originalUrl}`));
});

// 4. GLOBAL ERROR HANDLER (eng oxirgi — 2.8)
app.use(errorHandler);

export default app;

Misol 6 — Process-level handlerlar (2.12, 2.13)

js
// server.js
import app from "./app.js";
import { logger } from "./utils/logger.js";   // (5.12)

const server = app.listen(3000, () => logger.info("Server 3000-portda"));

// Ushlanmagan Promise (2.11-JS, 2.12)
process.on("unhandledRejection", (reason) => {
  logger.error("UNHANDLED REJECTION:", reason);
  server.close(() => process.exit(1));         // graceful 2.13-bob  PM2 restart (10.7)
});

// Ushlanmagan sinxron xato — programmer bug (2.2, 2.12)
process.on("uncaughtException", (err) => {
  logger.error("UNCAUGHT EXCEPTION:", err);
  process.exit(1);                              // darrov to'xta (2.12)
});

// Graceful shutdown (deploy — 2.13)
process.on("SIGTERM", () => {
  logger.info("SIGTERM qabul qilindi");
  server.close(() => {
    logger.info("Server yopildi");
    process.exit(0);
  });
});

Misol 7 — Sinxron throw (Express avtomatik ushlaydi — 2.5)

js
// SINXRON route'da throw — Express o'zi ushlaydi (next kerak emas)
app.get("/sync", (req, res) => {
  if (!req.query.id) throw new BadRequestError("id kerak");   // sinxron  handler (2.5)
  res.json({ id: req.query.id });
});
// LEKIN async'da — wrapper kerak 2.6-bob! Bu farqni esda tuting.

Misol 8 — Operational vs programmer (2.2)

js
// Operational (kutilgan) — isOperational: true  foydalanuvchiga javob
throw new NotFoundError("Mahsulot");          // 404, aniq javob (2.2)

// Programmer (bug) — oddiy Error  handler "Ichki server xatosi" + log (14)
const x = undefined;
x.foo;                                         // TypeError (bug)  500, log, yashir

// Handler ajratadi 2.4-bob:
//   err.isOperational ? err.message : "Ichki server xatosi"

5. To'g'ri va noto'g'ri holatlar

1) Har route'da try/catch + res (markazlashtirilmagan)

js
//  takror, har xil format (2.1)
app.get("/a", async (req, res) => { try {...} catch(e){ res.status(500).json({err: e.message}) } });

//  catchAsync + throw + global handler (2.6, 2.4)
app.get("/a", catchAsync(async (req, res) => { ...; throw new NotFoundError(); }));

2) Async xatoni ushlamaslik

js
//  async throw — Express 4 ushlamaydi  osiladi/qulaydi (2.6)
app.get("/x", async (req, res) => { await DB.query(); });

//  catchAsync wrapper
app.get("/x", catchAsync(async (req, res) => {...}));

3) Texnik xatoni foydalanuvchiga oshkor qilish

js
//  stack/DB xato javobda (production — 14, 2.10)
res.status(500).json({ error: err.stack });

//  prod'da umumiy, dev'da stack (5.8)

4) uncaughtException'da davom etish

js
//  log qilib, davom etish (ilova noto'g'ri holatda — 2.2, 2.12)
process.on("uncaughtException", (e) => console.error(e));   // exit yo'q!

//  log + exit + PM2 restart (2.12)
process.on("uncaughtException", (e) => { logger.error(e); process.exit(1); });

5) Error handler noto'g'ri joyda (3 argument)

js
//  route'dan oldin yoki 3 argument (ishlamaydi — 2.8, 5.6)
app.use((err, req, res) => {...});   // 3 argument!

//  4 argument, eng oxirda
app.use((err, req, res, next) => {...});

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Server bitta xatodan qulaydi

Sababi: ushlanmagan xato/rejection 2.12-bob. Yechimi: global handler + catchAsync 2.6-bob + process handlerlar 2.12-bob; PM2 10.7-bob.

Xato 2 — Async route'da xato "yo'qoladi" (so'rov osiladi)

Sababi: async throw ushlanmadi 2.6-bob. Yechimi: catchAsync wrapper yoki express-async-errors.

Xato 3 — Error handler ishlamaydi

Sababi: 3 argument yoki noto'g'ri joyda (2.8, 5.6). Yechimi: 4 argument; eng oxirda.

Xato 4 — Cannot set headers after they are sent

Sababi: javob yuborilgach, yana javob (yoki nextdan keyin — 5.5). Yechimi: return next(err); bitta javob; handler'da res.headersSent tekshir.

Xato 5 — Production'da maxfiy ma'lumot oshkor

Sababi: stack/DB xato javobda (2.10, 14). Yechimi: NODE_ENV bo'yicha yashiring 5.8-bob.

Xato 6 — Validatsiya/DB xatosi 500 bo'lib chiqadi

Sababi: tashqi xato moslanmagan 2.14-bob. Yechimi: handler'da err.name/err.code bo'yicha to'g'ri status (400/409 — 2.14).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • JS error handling 2.12-bob: custom error class, try/catch.
  • Express 5.6-bob: error middleware (4 argument), next(err).
  • REST 5.7-bob: izchil xato formati, to'g'ri status.
  • Validatsiya 5.9-bob: Zod xatosi 400 2.14-bob.
  • DB (6): Mongoose/SQL xatolarini moslash 2.14-bob.
  • Logger 5.12-bob: xatolarni log qilish (Winston/Pino).
  • Monitoring 10.9-bob: Sentry — xato kuzatuvi.
  • Deploy 10.7-bob: PM2 — crash'da restart.
  • NestJS 8.6-bob: Exception Filters — shu g'oya, avtomatik.

8. Eng yaxshi amaliyotlar (best practices)

  • Markazlashtirilgan global handler — route'lar faqat throw/next(err) 2.1-bob.
  • Custom error class iyerarxiyasi (statusCode, isOperational — 2.3).
  • Async — catchAsync wrapper (yoki express-async-errors / Express 5 — 2.6).
  • Operational vs programmer ni ajrating — operational: javob; programmer: log + ehtiyot 2.2-bob.
  • 4 argument, eng oxirda 2.8-bob; 404 handler undan oldin 2.7-bob.
  • Production'da texnik tafsilotni yashir (stack/DB — 14); to'liq log'da (2.10, 2.11).
  • Tashqi xatolarni moslash (Zod/Mongoose to'g'ri status — 2.14).
  • Process handlerlar (uncaughtException/unhandledRejection) + graceful shutdown + PM2 (2.12, 2.13).
  • Xatolarni log qil (Winston/Pino — 5.12; Sentry — 10.9).
  • return next(err)nextdan keyin kod ishlamasin.

9. Amaliy loyiha: "Professional Error Handling Tizimi"

Markazlashtirilgan, izchil error handling'ni mustahkamlash.

Maqsad

Custom error class, global handler, async wrapper, process-level handlerlar va xavfsiz xato javoblarini birlashtirib, barqaror, izchil xato tizimini qurish.

Talablar (requirements)

  1. Custom error class iyerarxiyasi: AppError + NotFound/BadRequest/Unauthorized/Forbidden/Conflict (statusCode, isOperational — Misol 1, 2.3).
  2. Async wrapper: catchAsync (Misol 2, 2.6); route'lar try/catch'siz, faqat throw.
  3. Global error handler: 4 argument, izchil javob 5.7-bob, prod'da tafsilotni yashiring (Misol 3, 2.10, 14).
  4. Tashqi xato moslash: Zod 5.9-bob 400, Mongoose CastError/duplicate (6) 400/409 2.14-bob.
  5. 404 handler: noma'lum yo'lga 2.7-bob; to'g'ri tartib (Misol 5, 2.8).
  6. Process handlerlar: unhandledRejection/uncaughtException + graceful shutdown (Misol 6, 2.12, 2.13).
  7. NODE_ENV xulq: dev'da stack, prod'da yashiring (5.8, 2.10).
  8. Operational vs programmer: ataylab ikkalasini yuzaga keltirib, handler farqini ko'rsating (Misol 8, 2.2).
  9. Logging: xatolarni log (console yoki Winston — 5.12); 5xx to'liq, 4xx qisqa.
  10. Route'lar toza bo'lsin — biznes logika + throw (markazlashgan — 2.1).

Maslahatlar (hint)

  • catchAsyncPromise.resolve(fn(...)).catch(next) (Misol 2).
  • Error handler 4 argument, app.js oxirida 2.8-bob.
  • Prod'da: statusCode >= 500 && isProd ? "Ichki xato" : message 2.10-bob.
  • Tashqi xato: err.name === "ZodError", err.code === 11000 2.14-bob.
  • Process: uncaughtException da process.exit(1) 2.12-bob.
  • return next(err)nextdan keyin davom etmasin.

"Tayyor" mezonlari (acceptance criteria)

  • Custom error iyerarxiyasi (status, isOperational).
  • catchAsync bilan async xatolar ushlanadi.
  • Global handler izchil javob; prod'da tafsilot yashirin.
  • Zod/Mongoose xatolari to'g'ri status'ga moslangan.
  • 404 handler + to'g'ri tartib.
  • Process handlerlar + graceful shutdown.
  • dev/prod xato javobi farqi.
  • Route'lar toza (try/catch'siz, faqat throw).
  • Xatolar log qilinadi.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda barqaror backend'ning ustunini — error handlingni o'rgandik:

  • Markazlashtirilgan — route'lar throw/next(err); bitta global handler ushlaydi (toza, izchil — 2.1).
  • Operational (kutilgan — javob) vs programmer (bug — log + ehtiyot — 2.2).
  • Custom error class (statusCode, isOperational — 2.3); global handler (4 argument, eng oxirda — 2.4, 2.8).
  • AsynccatchAsync wrapper (yoki express-async-errors / Express 5 — 2.6); 404 handler 2.7-bob.
  • Texnik tafsilotni yashir (prod — 14); tashqi xatolarni moslash (Zod/Mongoose — 2.14).
  • Process handlerlar (uncaughtException/unhandledRejection) + graceful shutdown + PM2 (2.12, 2.13); log 5.12-bob.

Keyingi bob — 5.11-bob: File upload — Multer (lokal va cloud). Asosiy backend infratuzilmasini (server, REST, validatsiya, error) qurdik; endi amaliy, ko'p uchraydigan vazifani — fayl yuklash (rasm, hujjat) ni — Multer bilan o'rganamiz: lokal saqlash, cloud (S3 — 10.6), tur/hajm tekshiruvi (14), va 5.4'dagi stream'lar bilan.


Foydalanilgan rasmiy/ishonchli manbalar

  • expressjs.com — Error Handling
  • Toptal — Node.js Error Handling Best Practices (operational vs programmer)
  • Better Stack — Express Error Handling Patterns; nodejs.org — process events

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
5.10-bob: Error handling middleware, global error handler — Wisar