Topic 15 of 55
Error Handling Patterns
Overview
Node.js has unique error handling requirements because errors can occur in async contexts where try/catch does not work. Proper error handling prevents crashes, data loss, and security vulnerabilities.
Syntax
javascript
// 1. Synchronous — try/catch
try {
JSON.parse(malformedJson);
} catch (err) {
console.error("Parse error:", err.message);
}
// 2. Async/await — try/catch
async function fetchData() {
try {
const res = await fetch(url);
if (!res.ok) throw new Error("HTTP " + res.status);
return await res.json();
} catch (err) {
logger.error({ err, url }, "Fetch failed");
throw err; // re-throw for caller to handle
}
}
// 3. Uncaught exceptions — last resort
process.on("uncaughtException", (err) => {
logger.fatal(err, "Uncaught exception");
process.exit(1); // MUST exit — state may be corrupted
});
process.on("unhandledRejection", (reason) => {
logger.error({ reason }, "Unhandled rejection");
process.exit(1);
});Common Pitfalls
- Never catch errors without handling them — empty catch blocks hide bugs silently.
- Operational errors (user errors, network failures) vs programmer errors (bugs) — operational errors should be caught and responded to; programmer errors should crash the process.
- Interview tip: process.on('unhandledRejection') is a safety net, not a replacement for proper error handling. Always handle rejections at the call site.
Real-World Example
Custom error classes for Express API error handling
example
javascript
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.name = this.constructor.name;
this.statusCode = statusCode;
this.code = code;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource) {
super(resource + " not found", 404, "NOT_FOUND");
}
}
class ValidationError extends AppError {
constructor(message, fields) {
super(message, 400, "VALIDATION_ERROR");
this.fields = fields;
}
}
// Express global error handler
function errorHandler(err, req, res, next) {
logger.error({ err, url: req.url }, "Request error");
if (err.isOperational) {
return res.status(err.statusCode).json({
error: { code: err.code, message: err.message },
});
}
res.status(500).json({ error: { code: "INTERNAL_ERROR", message: "Something went wrong" } });
}