Express menyediakan satu titik terpusat untuk menangani error yang muncul dari rute maupun middleware, yaitu middleware penanganan error dengan tanda tangan empat argumen (err, req, res, next). Topik ini membahas pendaftaran middleware tersebut, penanganan error sinkron lewat next(err), penangkapan otomatis error dari handler async pada Express 5, serta pola middleware 404 untuk rute yang tidak cocok. Setelah membaca topik ini, error dari seluruh rute dapat ditangani pada satu middleware terakhir dan respons error dapat berbentuk JSON yang konsisten.
Middleware penanganan error#
Middleware penanganan error dibedakan dari middleware biasa oleh jumlah argumennya, yaitu empat. Express mendeteksi middleware error secara eksplisit dari tanda tangan (err, req, res, next), sehingga argumen pertama adalah objek error. Middleware ini dipasang paling akhir, setelah seluruh rute dan middleware biasa, agar error dari mana pun dapat tertangkap di satu titik.
Pendaftaran dilakukan dengan app.use((err, req, res, next) => {...}). Bila beberapa middleware error didaftarkan, middleware tersebut hanya berjalan ketika middleware atau rute sebelumnya memanggil next(err) atau melempar error. Middleware error yang memanggil next(err) lagi akan meneruskan error ke middleware error berikutnya, sehingga beberapa lapisan pemrosesan error dapat dirangkai, misalnya satu lapisan untuk logging dan satu lapisan untuk format respons.
Penanganan error sinkron dan async#
Dua jalur masuk ke middleware error pada Express 5. Jalur pertama adalah pemanggilan next(err) secara eksplisit dari handler sinkron atau middleware biasa. Jalur kedua adalah throw di dalam handler async, yang ditangkap otomatis oleh Express 5 dan diteruskan sebagai next(err). Kedua jalur menyebabkan Express melewati middleware biasa berikutnya dan langsung melompat ke middleware error.
Diagram berikut menggambarkan aliran error. Handler rute yang melempar error atau memanggil next(err) meneruskan ke middleware error, yang mengirim respons 500. Secara terpisah, permintaan yang tidak cocok dengan rute mana pun ditangkap oleh middleware 404.
Berkas error-middleware.js menyatukan keempat kasus dalam satu aplikasi: handler async yang throw, handler sinkron yang next(err), middleware 404, dan middleware error sebagai titik terakhir.
// tidak-dijalankan: butuh Express yang dipasang di /tmp/middleware-error
const express = require('express');
const app = express();
app.use(express.json());
// handler async yang melempar error; Express 5 menangkapnya otomatis
app.post('/data', async (req, res) => {
if (!req.body.nama) throw new Error('nama wajib diisi');
res.json({ diterima: req.body.nama });
});
// handler sinkron yang meneruskan error lewat next(err)
app.get('/sync-error', (req, res, next) => {
next(new Error('error dari handler sinkron'));
});
// middleware 404: tercapai bila tidak ada rute yang cocok
app.use((req, res) => {
res.status(404).json({ error: 'sumber daya tidak ditemukan' });
});
// middleware penanganan error: tercapai bila ada next(err) atau throw
app.use((err, req, res, next) => {
console.log('tertangkap:', err.message);
res.status(500).json({ error: err.message });
});
const server = app.listen(0, async () => {
const port = server.address().port;
const a = await fetch(`http://127.0.0.1:${port}/data`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ nama: 'Ali' }),
});
const b = await fetch(`http://127.0.0.1:${port}/data`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({}),
});
const c = await fetch(`http://127.0.0.1:${port}/sync-error`);
const d = await fetch(`http://127.0.0.1:${port}/tidak-ada`);
console.log('POST /data (nama) ->', a.status, await a.text());
console.log('POST /data (kosong) ->', b.status, await b.text());
console.log('GET /sync-error ->', c.status, await c.text());
console.log('GET /tidak-ada ->', d.status, await d.text());
server.close();
});# jalankan dari direktori topik
NODE_PATH=/tmp/middleware-error/node_modules node error-middleware.jstertangkap: nama wajib diisi
tertangkap: error dari handler sinkron
POST /data (nama) -> 200 {"diterima":"Ali"}
POST /data (kosong) -> 500 {"error":"nama wajib diisi"}
GET /sync-error -> 500 {"error":"error dari handler sinkron"}
GET /tidak-ada -> 404 {"error":"sumber daya tidak ditemukan"}Dua baris tertangkap muncul lebih dahulu karena console.log di middleware error berjalan saat permintaan kedua dan ketiga diproses, sedangkan ringkasan di blok app.listen baru dicetak setelah seluruh await fetch selesai. Yang penting dari keluaran ini: handler async yang throw dan handler sinkron yang next(err) keduanya sampai ke middleware error yang sama, dan middleware 404 menangani rute yang tidak cocok tanpa memerlukan next(err).
Penanganan error async pada Express 4#
Pada Express 4, error dari handler async tidak ditangkap otomatis. Bila sebuah handler async melakukan await yang menolak dan penolakan tidak ditangani, proses Node memancarkan unhandledRejection dan permintaan menggantung. Pola yang umum dipakai adalah pembungkus try/catch yang meneruskan error lewat next(err).
// tidak-dijalankan: pola Express 4, bukan berkas utuh
app.get('/lama/:id', async (req, res, next) => {
try {
const data = await bacaDariSumber(req.params.id);
res.json(data);
} catch (err) {
next(err);
}
});Pada Express 5, pola di atas tetap sah dan tidak menyebabkan kerugian, tetapi try/catch tidak lagi wajib karena throw di handler async sudah diteruskan otomatis. Pemakaian try/catch tetap berguna bila handler perlu membersihkan sumber daya tertentu sebelum meneruskan error, atau bila handler ingin mengganti error internal dengan pesan yang lebih sesuai untuk klien.
Mengelola kode status pada respons error#
Middleware error bertanggung jawab atas kode status HTTP yang dikirim. Tidak ada pemetaan otomatis dari jenis error ke status, sehingga Error('nama wajib diisi') tetap menghasilkan 500 bila middleware menetapkan res.status(500). Untuk mengirim status yang berbeda, gunakan properti tambahan pada objek error atau periksa jenis error di middleware.
// tidak-dijalankan: fragmen, bukan berkas utuh
app.use((err, req, res, next) => {
const status = err.status || 500;
res.status(status).json({ error: err.message });
});Konvensi yang umum dipakai adalah menetapkan err.status pada handler sebelum meneruskan, misalnya const err = new Error('tidak ditemukan'); err.status = 404; next(err);. Middleware error membaca err.status dan mengirim kode yang sesuai, sehingga satu middleware dapat menangani 400, 404, 409, dan 500 sekaligus tanpa rute tambahan.
Kesalahan umum#
Mendaftarkan middleware error sebelum rute. Karena daftar diproses berurutan, middleware error yang dipasang lebih awal tidak akan menangkap error dari rute yang didaftarkan setelahnya. Pasang middleware error sebagai app.use terakhir, setelah middleware 404 jika ada.
Mengabaikan middleware 404. Bila tidak ada middleware 404, permintaan ke jalur yang tidak terdaftar mendapat respons default Express berupa Cannot GET /jalur dengan status 404 berformat HTML. Tambahkan app.use((req, res) => res.status(404).json({ error: '...' })) agar respons 404 berbentuk JSON yang konsisten dengan middleware error.
Mengandalkan try/catch pada Express 5 tanpa memeriksa kontrak pustaka luar. Walaupun throw dari handler async ditangkap otomatis, pustaka berbasis callback lama yang memancarkan error lewat event 'error' (misalnya stream yang gagal) tidak tertangkap oleh mekanisme ini. Gunakan stream.pipeline atau once(stream, 'error') untuk kasus demikian.
Ringkasan#
- Middleware penanganan error memiliki tanda tangan
(err, req, res, next)dan dipasang paling akhir agar error dari seluruh rute tertangkap di satu titik. - Pada Express 5, handler async yang melempar error ditangkap otomatis dan diteruskan ke middleware error, sedangkan pada Express 4 pola
try/catchdengannext(err)masih wajib. next(err)adalah jalur umum untuk meneruskan error dari handler sinkron atau middleware biasa.- Middleware 404 dipasang setelah seluruh rute dan sebelum middleware error untuk menangani permintaan tanpa rute yang cocok, mengirim respons 404 tanpa memicu middleware error.
Referensi#
- Express — Error Handling — Express.js — Penjelasan resmi middleware penanganan error, tanda tangan empat argumen, dan middleware 404.
- Express 5.x API — app.use — Express.js — Catatan resmi tentang pendaftaran middleware error di posisi terakhir.
- Migrating to Express 5 — Express.js — Perubahan perilaku handler async pada Express 5, termasuk penanganan promise yang ditolak.