farkmt2official 1
farkmt2official
mannaxxx 1
mannaxxx
Bvural41 1
Bvural41
Agora Metin2 1
Agora Metin2
berkmenoo 1
berkmenoo
Hikaye Ekle

[NODE JS] Express Error Handling Middleware

nodejs.png


[NODE JS] Express Error Handling Middleware​


Hata yönetimi yazılım geliştirmenin en önemli ama en çok ihmal edilen konularından biri. Bir uygulamanın kalitesini mutlu yoldan çok hata durumlarındaki davranışı belirler. Express'te hata yönetimi middleware sistemi üzerine kurulu ve doğru kullanıldığında çok güçlü bir mekanizma sunuyor.

Express'te error handling middleware'i normal middleware'lerden farklı olarak dört parametre alır: err, req, res, next. Express bu dört parametreli fonksiyonları otomatik olarak error handler olarak tanır. Bu yüzden parametre sayısını doğru tutmanız çok önemli. Üç parametre yazarsanız normal middleware olur ve hataları yakalayamaz.

Error handling middleware her zaman en son tanımlanmalıdır. Tüm route'lardan ve diğer middleware'lerden sonra gelmelidir. Çünkü Express hata oluştuğunda pipeline'daki sonraki error handler'ı arar. Eğer error handler route'lardan önce tanımlıysa çalışmaz.

Express'te hata iki şekilde error handler'a ulaşır: next(error) çağrısı ile veya throw ile. Senkron kodda throw kullanabilirsiniz, Express bunu otomatik yakalar. Ama asenkron kodda (callback, Promise) throw çalışmaz, next(error) kullanmanız gerekir. Express 5 async fonksiyonlardaki hataları otomatik yakalar ama Express 4'te bu desteği kendiniz eklemeniz lazım.

Ben bir production projesinde error handling'i geç eklemiştim. Uygulama her hatada stack trace'i kullanıcıya gösteriyordu. Bu hem güvenlik açığı (internal bilgilerin sızması) hem de kötü kullanıcı deneyimi. O zamandan beri her projeye en başından error handling ekliyorum. Production'da asla stack trace göstermeyin, sadece kullanıcı dostu bir mesaj ve hata kodu gösterin.

Operational errors ve programming errors arasındaki farkı bilmek önemli. Operational errors beklenen hatalardır: veritabanı bağlantısı kopması, geçersiz kullanıcı girişi, dosya bulunamadı gibi. Bunları graceful şekilde handle edebilirsiniz. Programming errors ise bug'lardır: undefined bir değişkene erişme, tip hatası gibi. Bunları yakalamak yerine düzeltmeniz gerekir.

Birden fazla error handler tanımlayabilirsiniz. Her biri next(err) ile hatayı sonraki handler'a iletebilir. Bu pattern ile hataları kategorize edebilirsiniz: biri loglama yapan, biri response gönderen, biri bildirim atan gibi. Bu ayrım özellikle büyük projelerde kodu temiz tutar.

Unhandled rejection ve uncaught exception yakalamayı da unutmayın. Express middleware pipeline'ının dışında kalan hatalar için process seviyesinde event listener'lar eklemeniz gerekir. Ama bu listener'larda process'i kapatmayı düşünün çünkü uygulama tutarsız bir durumda olabilir.

Custom error sınıfları oluşturmak kodunuzu çok daha düzenli hale getirir. AppError, ValidationError, NotFoundError gibi özel hata sınıfları ile farklı hata türlerini kolayca ayırt edebilirsiniz. Her sınıfın kendi status code'u, mesajı ve ek bilgileri olabilir.

JavaScript:
const express = require('express');
const app = express();

app.use(express.json());

// ===== OZEL HATA SINIFLARI =====

class AppError extends Error {{
  constructor(message, statusCode, code) {{
    super(message);
    this.statusCode = statusCode;
    this.code = code || 'UNKNOWN_ERROR';
    this.isOperational = true;
    Error.captureStackTrace(this, this.constructor);
  }}
}}

class NotFoundError extends AppError {{
  constructor(kaynak = 'Kaynak') {{
    super(`${{kaynak}} bulunamadi`, 404, 'NOT_FOUND');
  }}
}}

class ValidationError extends AppError {{
  constructor(hatalar) {{
    super('Validasyon hatasi', 400, 'VALIDATION_ERROR');
    this.hatalar = hatalar;
  }}
}}

class UnauthorizedError extends AppError {{
  constructor(mesaj = 'Kimlik dogrulanmadi') {{
    super(mesaj, 401, 'UNAUTHORIZED');
  }}
}}

class ForbiddenError extends AppError {{
  constructor(mesaj = 'Erisim engellendi') {{
    super(mesaj, 403, 'FORBIDDEN');
  }}
}}

// ===== ASYNC HANDLER WRAPPER =====
// Express 4'te async hatalari yakalamak icin

function asyncHandler(fn) {{
  return (req, res, next) => {{
    Promise.resolve(fn(req, res, next)).catch(next);
  }};
}}

// ===== ORNEK VERILER =====

const urunler = [
  {{ id: 1, ad: 'Laptop', fiyat: 25000 }},
  {{ id: 2, ad: 'Telefon', fiyat: 18000 }},
];

// ===== ROUTE'LAR =====

// Senkron hata - Express otomatik yakalar
app.get('/api/senkron-hata', (req, res) => {{
  throw new AppError('Bu senkron bir hatadir', 500, 'SYNC_ERROR');
}});

// Async hata - asyncHandler ile sarmalanmis
app.get('/api/urunler', asyncHandler(async (req, res) => {{
  // Veritabani simule (gercekte async islem olur)
  await new Promise(resolve => setTimeout(resolve, 100));
  res.json(urunler);
}}));

app.get('/api/urunler/:id', asyncHandler(async (req, res) => {{
  const id = parseInt(req.params.id, 10);

  if (isNaN(id)) {{
    throw new ValidationError([{{ alan: 'id', mesaj: 'ID sayi olmalidir' }}]);
  }}

  const urun = urunler.find(u => u.id === id);
  if (!urun) {{
    throw new NotFoundError('Urun');
  }}

  res.json(urun);
}}));

app.post('/api/urunler', asyncHandler(async (req, res) => {{
  const {{ ad, fiyat }} = req.body;
  const hatalar = [];

  if (!ad || typeof ad !== 'string' || ad.trim().length < 2) {{
    hatalar.push({{ alan: 'ad', mesaj: 'Urun adi en az 2 karakter olmalidir' }});
  }}
  if (!fiyat || typeof fiyat !== 'number' || fiyat <= 0) {{
    hatalar.push({{ alan: 'fiyat', mesaj: 'Fiyat pozitif bir sayi olmalidir' }});
  }}

  if (hatalar.length > 0) {{
    throw new ValidationError(hatalar);
  }}

  const yeniUrun = {{ id: urunler.length + 1, ad: ad.trim(), fiyat }};
  urunler.push(yeniUrun);
  res.status(201).json(yeniUrun);
}}));

// ===== 404 HANDLER =====
// Hicbir route'a uymayanlar icin

app.use((req, res, next) => {{
  next(new NotFoundError(`${{req.method}} ${{req.url}} endpoint'i`));
}});

// ===== ERROR LOGGING MIDDLEWARE =====

app.use((err, req, res, next) => {{
  // Hatayi logla
  const logEntry = {{
    timestamp: new Date().toISOString(),
    requestId: req.requestId || 'N/A',
    method: req.method,
    url: req.originalUrl,
    error: {{
      message: err.message,
      code: err.code,
      statusCode: err.statusCode,
      stack: err.stack,
    }},
    ip: req.ip,
  }};

  if (err.statusCode >= 500 || !err.isOperational) {{
    console.error('[KRITIK HATA]', JSON.stringify(logEntry, null, 2));
  }} else {{
    console.warn('[ISTEMCI HATASI]', `${{err.statusCode}} ${{err.message}}`);
  }}

  next(err); // Sonraki error handler'a ilet
}});

// ===== ANA ERROR HANDLER =====

app.use((err, req, res, next) => {{
  const statusCode = err.statusCode || 500;
  const isDev = process.env.NODE_ENV !== 'production';

  const response = {{
    basarili: false,
    hata: {{
      mesaj: statusCode >= 500 && !isDev
        ? 'Sunucu hatasi olustu'
        : err.message,
      kod: err.code || 'INTERNAL_ERROR',
    }},
  }};

  // Validasyon hatalarinda detaylari ekle
  if (err instanceof ValidationError) {{
    response.hata.detaylar = err.hatalar;
  }}

  // Gelistirme ortaminda stack trace goster
  if (isDev) {{
    response.hata.stack = err.stack;
  }}

  res.status(statusCode).json(response);
}});

// ===== PROCESS SEVIYESI HATA YAKALAMA =====

process.on('unhandledRejection', (reason, promise) => {{
  console.error('Yakalanmamis Promise Rejection:', reason);
}});

process.on('uncaughtException', (err) => {{
  console.error('Yakalanmamis Exception:', err);
  process.exit(1);
}});

app.listen(3000, () => {{
  console.log('Error handling sunucusu port 3000\'de');
}});

Hata yönetimi bir düşünce biçimidir. Kod yazarken her zaman "ya bu başarısız olursa?" sorusunu sorun. Network istekleri başarısız olabilir, veritabanı bağlantısı kopabilir, dosya bulunamayabilir, kullanıcı geçersiz veri gönderebilir. Bu senaryoların hepsini önceden düşünüp ele almak sağlam bir uygulama oluşturmanın temelidir.
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Benzer konular

  • 5.00 yıldız(lar) 1 Değerlendirme
2 3
Cevaplar
12
Görüntüleme
1K
Geri
Üst