Belajar ExpressJS - Error Handling
Episode 7 of 28

Belajar ExpressJS - Error Handling

Membangun error handling yang benar: middleware error empat argumen, async error handling otomatis Express 5, custom error class, dan centralized error handler untuk response error yang konsisten.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Setelah di episode 6 kalian membangun endpoint dengan response lengkap, episode 7 menjawab pertanyaan yang tak terhindarkan: apa yang terjadi saat sesuatu gagal? Database down, input tak terduga, bug — semua aplikasi produksi menghadapinya. Yang membedakan aplikasi profesional adalah cara kegagalan ditangani.

Mengapa error handling layak mendapat episode sendiri? Karena error yang tidak tertangani di Node.js bisa mematikan proses (uncaught exception) atau membuat request menggantung tanpa response. Error handler terpusat memastikan: semua error ditangkap di satu tempat, response error konsisten, dan detail teknis tidak bocor ke client.

Dasar: Middleware Error Empat Argumen

Middleware error punya empat parametererr, req, res, next — dan dipasang di paling akhir pipeline:

JSMiddleware error dasar
app.use((req, res, next) => {
  res.status(404).json({ error: "NOT_FOUND", message: "Route tidak ditemukan" })
})
 
app.use((err, req, res, next) => {
  console.error(err)
  res.status(500).json({ error: "INTERNAL_ERROR", message: "Terjadi kesalahan" })
})

Urutan di atas penting: 404 handler menangkap semua request yang tidak cocok route mana pun, lalu error handler (empat argumen) menangkap semua error. Express membedakannya murni dari jumlah parameter fungsi.

Mengirim Error ke Handler: next(err)

Middleware dan handler mengirim error dengan memanggil next(err):

JSMeneruskan error ke error handler
app.get("/users/:id", (req, res, next) => {
  const id = Number(req.params.id)
 
  if (!Number.isInteger(id)) {
    const err = new Error("ID tidak valid")
    err.status = 400
    return next(err)
  }
 
  next()
})

Begitu next(err) dipanggil, Express melompati seluruh pipeline normal dan langsung menjalankan error handler terdekat. Request yang memanggil next(err) tidak perlu mengirim response sendiri.

Async Error Handling: Kekuatan Express 5

Masalah di Express 4

Sebelum Express 5, error dari Promise di handler async tidak otomatis diteruskan:

JSExpress 4: harus dibungkus try/catch
app.get("/users", async (req, res, next) => {
  try {
    const users = await db.findMany()
    res.json(users)
  } catch (err) {
    next(err)
  }
})

Melupakan try/catch membuat rejection menjadi unhandled rejection — request menggantung, dan di Node modern bisa menghentikan proses.

Solusi Express 5

Express 5 secara otomatis menangkap rejection dari handler async dan meneruskannya ke error handler:

JSExpress 5: rejection otomatis diteruskan
app.get("/users", async (req, res, next) => {
  const users = await db.findMany()
  res.json(users)
})

Jika db.findMany() melempar error, Express 5 otomatis memanggil error handler tanpa try/catch manual. Ini alasan utama banyak tim bermigrasi ke Express 5 — kode async jadi jauh lebih bersih.

Note

Express 5 menangkap rejection di handler async, tetapi bukan untuk Promise yang kalian lepas tanpa await (fire-and-forget). Untuk operasi latar belakang seperti itu, tangani rejection sendiri — topik ini kembali di episode 22 saat membahas background jobs.

Custom Error Class

Agar error bisa membawa status dan detail, buat class error sendiri:

JSsrc/utils/AppError.js
export class AppError extends Error {
  constructor(status, code, message) {
    super(message)
    this.status = status
    this.code = code
    this.name = "AppError"
  }
}
 
export const notFound = (id) =>
  new AppError(404, "USER_NOT_FOUND", `User ${id} tidak ditemukan`)

Error ini lalu dilempar langsung di handler:

JSMelempar AppError
import { AppError } from "./utils/AppError.js"
 
app.get("/users/:id", async (req, res, next) => {
  const user = await db.findById(req.params.id)
  if (!user) throw new AppError(404, "USER_NOT_FOUND", "User tidak ditemukan")
  res.json(user)
})

Dengan Express 5, throw di dalam handler async langsung ditangkap — tidak perlu return next(err).

Centralized Error Handler

Error handler terpusat membaca properti custom error dan menyeragamkan bentuk response:

JSsrc/middleware/errorHandler.js
import { AppError } from "../utils/AppError.js"
 
export const errorHandler = (err, req, res, next) => {
  const status = err.status || 500
  const code = err.code || "INTERNAL_ERROR"
 
  if (status >= 500) {
    console.error(err)
  }
 
  res.status(status).json({
    error: code,
    message: err.message,
    ...(process.env.NODE_ENV === "development" ? { stack: err.stack } : {}),
  })
}

Tiga keputusan desain yang penting:

  • Error 5xx di-log — karena menandakan bug, sementara 4xx umumnya bukan tanggung jawab server.
  • Bentuk response konsisten — selalu { error, message }, konsisten dengan episode 6.
  • Stack trace hanya di development — detail internal tidak pernah bocor ke client produksi.

Handler ini dipasang terakhir di app.js, setelah semua route:

JSPemasangan error handler
import { errorHandler } from "./middleware/errorHandler.js"
 
app.use("/api", apiRouter)
app.use((req, res) => res.status(404).json({ error: "NOT_FOUND", message: "Route tidak ditemukan" }))
app.use(errorHandler)

Alur Error Lengkap

100%

Common Pitfalls

Melupakan next pada Middleware Biasa

Middleware yang error tapi memanggil next() tanpa argumen justru meneruskan request normal — bukan error. Selalu next(err) saat ingin masuk jalur error.

Error Handler Dipasang di Tengah

Error handler hanya menangkap error dari middleware/router yang berada di atasnya. Pasang di paling akhir setelah semua route.

Mengembalikan Detail Teknis

Mengirim err.stack atau pesan database mentah ke client membocorkan struktur internal. Batasi ke kode dan pesan yang aman untuk publik.

Tip

Log error 5xx dengan struktur lengkap (episode 17) — stack trace di log server, bukan di response. Client hanya perlu tahu ada kesalahan; tim kalian yang perlu detail.

Penutup

Episode 7 membangun fondasi ketahanan aplikasi: middleware error empat argumen, next(err) sebagai pintu masuk jalur error, async error handling otomatis Express 5, AppError sebagai bahasa error bersama, dan centralized error handler sebagai satu-satunya gerbang response error.

Inti yang harus dibawa pulang:

  • Error middleware punya 4 argumen dan dipasang paling akhir.
  • next(err) melompati pipeline normal menuju error handler.
  • Express 5 otomatis menangkap rejection handler async — kode lebih bersih.
  • AppError(status, code, message) membawa konteks error lintas lapisan.
  • Error handler terpusat: log 5xx, sembunyikan stack di produksi, bentuk response konsisten.

Di episode 8 selanjutnya kita akan membawa struktur aplikasi ke level berikutnya: router dan modular appexpress.Router(), pemecahan route per resource, dan refactor aplikasi Hello World menjadi modul yang rapi. Sampai jumpa di episode 8!

Belajar ExpressJS - Error Handling | Belajar ExpressJS