Skip to solution
mediumLow-Level Design

How do you handle errors in Node.js applications?

667 views
01

Understand the problem

Question presented to candidate: "An async Express route handler throws an error with no try/catch around it. What happens to that request, and does your answer change between Express 4 and Express 5?"

What a strong answer should cover:

  • Node.js error handling is not one mechanism — it differs by context: synchronous code uses try/catch; callback-style async code uses the error-first (err, result) convention; Promise-based/async code uses try/catch around await, or .catch().
  • 📌 A version-specific, verifiable gap: in Express 4, a synchronous throw inside a route handler is automatically caught and routed to centralized error-handling middleware — but an async handler that throws (or rejects) with no manual next(err) call is not caught automatically; it crashes the process as an uncaught exception. Express 5 fixes this specific gap, automatically forwarding a rejected promise from an async handler to next().
  • Centralized error-handling middleware in Express is a 4-argument function (err, req, res, next) — Express recognizes this specific arity and routes errors to it, distinct from ordinary 3-argument middleware.
  • The operational vs. programmer error distinction (covered fully in its own dedicated question) determines the right response once an error is caught: an operational error should be handled and answered with an appropriate status code; a programmer error/bug should generally be logged and allowed to crash the process for a supervisor to restart, rather than papered over.
  • uncaughtException/unhandledRejection handlers at the process level are a last-resort safety net — logging and exiting — not a substitute for handling errors correctly at the point they actually occur.
  • A precise answer names the practical mitigation for the Express-4 async gap specifically: wrap every async handler in a small utility (a manual try/catch calling next(e), or a wrapper like express-async-handler) until/unless the codebase is on Express 5.

Clarifying questions expected:

  • "Which Express major version, or a different framework entirely?" — the async-handler auto-catch behavior is genuinely version-dependent, verified above.
  • "Is the concern about a specific caught error's response, or the broader process-level safety net?" — these are different layers of the same overall topic.

Code / implementation expected: Yes — actually demonstrating the Express 4 async-handler gap (a real crash) versus the manual-next(e) fix is the concrete, convincing part of the answer.

error handlingasynchronouspromisesbest practices
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js interviews — assumes basic Express and Promise familiarity. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The Express behavior below was actually executed agai

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real Express 4 server: sync throw auto-caught, async+next(e) forwarded correctly, unhandled async throw crashes the process
const express = require("express");
const app = express();

app.get("/sync-throw", (req, res) => { throw new Error("sync error"); });

app.get("/async-throw-manual", async (req, res, next) => {
  try { throw new Error("async error"); } catch (e) { next(e); }
});

app.get("/async-unhandled", async (req, res) => {
  throw new Error("unhandled async error"); // Express 4: NOT caught, crashes the process
});

app.use((err, req, res, next) => { // 4 args = error middleware, recognized by arity
  res.status(500).json({ error: err.message });
});

// sync-throw               -> 500 { error: "sync error" }
// async-throw-manual       -> 500 { error: "async error" }
// async-unhandled          -> process CRASHES (Express 4 has no auto-catch here)

// The fix, until on Express 5:
const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);
app.get("/safe", wrap(async (req, res) => { throw new Error("now correctly forwarded"); }));
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 77 of 152 decoded in the Node.js track. One more won't hurt.

Back to track