✅ Goals Implemented:
1. ✅ Centralized error handler
2. ✅ Hide internal error details from clients
3. ✅ Use proper HTTP status codes
4. ✅ Avoid app crash on unhandled errors
📁 Project Structure
project/
│
├── app.js
├── utils/
│ └── AppError.js
├── middleware/
│ └── errorHandler.js
├── routes/
│ └── userRoutes.js
└── server.js
🧱 Step-by-Step Implementation
🔹 utils/AppError.js — Custom Error Class
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode || 500;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
🔹 middleware/errorHandler.js — Centralized Error Middleware
const AppError = require('../utils/AppError');
module.exports = (err, req, res, next) => {
const statusCode = err.statusCode || 500;
// Do not expose internal errors in production
const response = {
status: 'error',
statusCode,
message:
process.env.NODE_ENV === 'development' ? err.message : 'Something went
wrong'
};
if (process.env.NODE_ENV === 'development') {
response.stack = err.stack;
}
res.status(statusCode).json(response);
};
🔹 routes/userRoutes.js — Sample Route with Error
const express = require('express');
const router = express.Router();
const AppError = require('../utils/AppError');
router.get('/users', (req, res, next) => {
try {
// Simulated error
throw new AppError('User data not found', 404);
} catch (err) {
next(err);
}
});
module.exports = router;
🔹 app.js — App Setup
const express = require('express');
const app = express();
const userRoutes = require('./routes/userRoutes');
const errorHandler = require('./middleware/errorHandler');
const AppError = require('./utils/AppError');
// Parse JSON
app.use(express.json());
// Routes
app.use('/api', userRoutes);
//we need to specify the regular expression directly and not as a string and
that was the issue
// 404 Handler for unknown routes
app.all(/(.*)/, (req, res, next) => {
next(new AppError(`Cannot find ${req.originalUrl} on this server`, 404));
});
// Centralized Error Handler
app.use(errorHandler);
module.exports = app;
🔹 server.js — Entry Point with Crash Protection
require('dotenv').config();
const app = require('./app');
const PORT = process.env.PORT || 3000;
// Start server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Global handler for unhandled exceptions
process.on('uncaughtException', (err) => {
console.error('Uncaught Exception:', err.message);
process.exit(1);
});
process.on('unhandledRejection', (err) => {
console.error('Unhandled Rejection:', err.message);
// Optional: Gracefully close server
});
✅ Test Example
1. Run the app:
NODE_ENV=production node server.js
2. Visit:
http://localhost:3000/api/users – Simulates 404 error
http://localhost:3000/some/invalid/route – Triggers global 404
🧠 Summary
Principle Implemented in
Centralized error handling errorHandler.js
No internal error exposure Controlled in NODE_ENV !== 'development'
Proper HTTP status codes AppError class
App doesn't crash on errors server.js process event handlers