Loading lessons...
Custom AppError Class & Operational Errors
Operational vs Programming Errors
In Node.js backends, errors fall into two main categories:
- Operational Errors: Predictable, expected runtime errors (e.g. invalid user input, 404 Not Found, 401 Unauthorized, duplicate key).
- Programming Errors: Bugs in developer code (e.g.
TypeError: cannot read property of undefined, syntax errors).
Creating a Custom AppError Class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith("4") ? "fail" : "error";
this.isOperational = true; // Identifies known operational errors
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
Using AppError in Routes & Error Middleware
const AppError = require("./AppError");
app.get("/product/:id", asyncHandler(async (req, res) => {
const product = await Product.findById(req.params.id);
if (!product) {
throw new AppError("No product found with that ID", 404);
}
res.json(product);
}));
// Global Error Handler
app.use((err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.status = err.status || "error";
if (err.isOperational) {
// Trusted operational error: send message to client
return res.status(err.statusCode).json({
status: err.status,
message: err.message
});
}
// Programming or unknown error: don't leak details in production!
console.error("ERROR 💥", err);
return res.status(500).json({
status: "error",
message: "Something went very wrong!"
});
});
TL;DR
- Create an
AppErrorclass inheriting fromErrorto attach HTTP status codes. - Mark predictable client errors as
isOperational = true. - Hide raw stack traces for unknown programming errors in production environments.