Express hataları dört argüman alan, (err, req, res, next), ve tüm route'larından sonra kaydedilen özel bir middleware ile yönetir. Express ona bir route içinde fırlatılan her hatayı, next(err) ile ilettiğin her hatayı ve Express 5'te bir async handler'dan gelen her reject edilmiş promise'i gönderir. Handler status code'a karar verir ve tek bir hata response'u gönderir, böylece kodunun geri kalanı sadece hata fırlatabilir.
GET /users/99, {"error":"No user with id 99"} ile 404 döndürür ve Console'da hata handler'ının yazdığı satır görünür. GET /users/1 gönderirsen route normal cevap verir; hata handler'ı yalnızca bir şey ters gittiğinde çalışır.
Express'in senin için yakaladığı hatalar
Bir route'tan hata handler'ına ulaşmanın üç yolu vardır ve Express 5'te üçü de aynı çalışır:
GET /async, {"error":"Rejected in an async route"} ile 500 döndürür; GET /sync ve GET /next de kendi mesajlarıyla aynı handler'da biter. Şimdi hata handler'ının parametrelerinden next'i sil ve GET /sync isteğini tekrar gönder: üç parametreyle Express onu sıradan bir middleware sayar, atlar ve varsayılan HTML hata sayfasıyla cevap verir.
Async hatalar: Express 5 ve Express 4
/async route'u çalışır, çünkü Express 5 reject edilen promise'i yakalar. Express 4 yakalamıyordu: hata unhandled rejection'a dönüşüyor ve istek asılı kalıyordu.
| Express 4 | Express 5 | |
|---|---|---|
Senkron handler'da throw | Yakalanır | Yakalanır |
next(err) | Yakalanır | Yakalanır |
async handler'dan reject edilmiş promise | Yakalanmaz, istek asılı kalır | Yakalanır |
setTimeout ya da olay callback'inde hata | Yakalanmaz, süreç çöker | Yakalanmaz, süreç çöker |
Express 4'te her async handler'ı sar ya da express-async-errors paketini kur:
// Express 4 only
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.findUser(req.params.id);
res.json(user);
}));
Son satır iki sürümde de aynıdır: Express kendisinin çağırmadığı bir callback'te fırlatılan hatayı göremez. Callback API'lerini promise'e çevir (Node'un çoğunun fs/promises gibi bir promise sürümü vardır) ve onları await et.
404 handler'ı ve tutarlı bir JSON hata
Her hatanın aynı yapıda olduğu bir API'yi kullanmak daha kolaydır. Hatalara bir status ver, eşleşmeyen route'ları 404 hatasına çevir ve hepsini tek bir handler biçimlendirsin:
GET /nope hiçbir route ile eşleşmez, bu yüzden 404 handler'ı onu bir hataya çevirir ve cevap {"error":"Cannot GET /nope","status":404} olur. GET /users/99 gönderirsen route'un fırlattığı hata da aynı yapıyı alır. {"name": } body'siyle POST /users gönder: express.json() onu parse edemez ve status değeri 400 olan bir hatayla next() çağırır, bu yüzden cevap {"error":"Unexpected token '}', \"{\"name\": }\" is not valid JSON","status":400} olur.
Stack trace sızdırma
Kendi HttpError sınıfından gelen hata mesajları istemci için yazılmıştır. Diğer her şeyden gelen mesajlar ve stack'ler (bir veritabanı sürücüsü, TypeError fırlatan bir yazım hatası) dosya yollarını, sorguları ve kütüphane sürümlerini ortaya çıkarabilir. Yukarıdaki handler her 5xx için Internal server error gönderir ve asıl hatayı sunucuda log'a yazar. if (res.headersSent) return next(err); satırı, response'un bir kısmı zaten gönderildikten sonra oluşan bir hatayı karşılar: o noktada tek güvenli hamle bağlantıyı Express'in kapatmasına izin vermektir.
Express'in kendi varsayılan handler'ı NODE_ENV değerine bakar: NODE_ENV değeri production değilse stack trace'i HTML sayfasına ekler. Bunu production sunucunda ayarla (NODE_ENV=production node server.js), ama buna güvenme: err.stack değerini hiç göndermeyen özel bir handler daha güvenlidir.
Sık yapılan hatalar
Hata handler'ını route'lardan önce kaydetmek. Express hataları yalnızca ileri iletir, bu yüzden bir route'un üstündeki handler o route'un hatalarını hiç görmez. Onu en sona, 404 handler'ından sonra koy.
Bir hatayı yakalayıp onunla hiçbir şey yapmamak. Sadece log yazan bir try/catch isteği asılı bırakır. catch içinde next(err) çağır, hatayı tekrar fırlat ya da bir response gönder.
İki response göndermek. res.json() sonrasında hata fırlatmak, hata handler'ının tekrar göndermeye çalışmasına neden olur. Örnekteki gibi res.headersSent değerini kontrol et.
Sıradan middleware'in ve next() fonksiyonunun nasıl çalıştığı için Express middleware sayfasına bak.
Sıkça Sorulan Sorular
Express'te birden fazla hata handler'ı olabilir mi?
Evet. Sırayla çalışırlar ve her biri ya bir response gönderir ya da hatayı iletmek için next(err) çağırır. Yaygın bir ayrım, log yazan bir handler ile response gönderen bir handler'dır.
Bir hata handler'ı hata fırlatırsa ne olur?
Express yeni hatayı bir sonraki hata handler'ına, o yoksa da 500 sayfası gönderen kendi yerleşik handler'ına iletir. Hata handler'larını kendileri hata veremeyecek kadar basit tut.
Hata response'unda hangi status code kullanılmalı?
İstemci yanlış bir şey gönderdiğinde 4xx kullan (400 hatalı girdi, 401 giriş yapılmamış, 403 izin yok, 404 bulunamadı, 409 çakışma), kodun hata verdiğinde 500. Body'de hata olan bir 200 ile asla cevap verme.
Birkaç alan için doğrulama hatalarını nasıl döndürürüm?
Bir liste ile 400 döndür, örneğin { "errors": [{ "field": "email", "message": "is required" }] }. zod kullanıyorsan, her route hatalı girdiyi aynı şekilde bildirsin diye hata handler'ında result.error.issues değerini bu yapıya çevir.