Global Error Handler Refinement: Dev vs Prod Error Messages
Refine your Express global error handler to safely distinguish between dev and prod environments. Log detailed errors while keeping API responses clean.

Previously in this course, we implemented Advanced Error Handling to catch exceptions in a single location. While that setup works, it currently treats every error the same, potentially leaking sensitive stack traces to users when things break.
In this lesson, we will refine our global error handler to provide meaningful, environment-specific feedback. We’ll ensure our developers get the information they need to debug, while our API consumers receive sanitized, professional error messages.
Why Separate Dev and Prod Error Messages?
When an error occurs in development, you want the full stack trace: file paths, line numbers, and the chain of function calls. This is your primary diagnostic tool.
However, exposing that same data in production is a massive security risk. Attackers can use stack traces to map out your file structure, identify library versions, or find vulnerable logic. We need a way to detect the environment and toggle the output accordingly.
The Environment-Aware Middleware
We use the NODE_ENV variable (which we covered in Environment Variables) to decide how to respond.
JAVASCRIPT// middleware/errorMiddleware.js const globalErrorHandler = (err, req, res, next) => { // Default to 500 if no status code is set const statusCode = err.statusCode || 500; // Log the full error for server-side monitoring console.error(CE9178">`[Error] ${statusCode} - ${err.message}`); console.error(err.stack); if (process.env.NODE_ENV === CE9178">'development') { // Detailed response for devs res.status(statusCode).json({ status: CE9178">'error', message: err.message, stack: err.stack, error: err }); } else { // Sanitized response for production res.status(statusCode).json({ status: CE9178">'error', message: statusCode === 500 ? CE9178">'Something went wrong' : err.message }); } }; module.exports = globalErrorHandler;
Logging Detailed Errors

While the API response is sanitized, your server logs (often viewed in services like Render or Datadog) should remain verbose. Think of these logs as your "black box" flight recorder.
In production, avoid console.log for errors. Use a structured logger like winston or pino if you are building a production-grade system. For now, continuing to log the err.stack inside our middleware ensures that even when a user sees a generic "Something went wrong" message, you have the exact file and line number waiting for you in your dashboard.
Comparing Error Responses
| Feature | Development Response | Production Response |
|---|---|---|
| Status Code | Original (e.g., 400, 500) | Original |
| Message | Specific Error Detail | Sanitized Message |
| Stack Trace | Included | Hidden |
| Primary Goal | Debugging | Security / Privacy |
Hands-on Exercise: Refine the Handler
- Open your existing
middleware/errorMiddleware.jsfile. - Update the logic to include the
if (process.env.NODE_ENV === 'development')condition as shown above. - Test your setup:
- Set
NODE_ENV=developmentin your.envfile and trigger an error—you should see the stack trace in Postman. - Change it to
NODE_ENV=productionand restart your server—you should see a clean JSON response without the stack trace.
- Set
Common Pitfalls
- Forgetting to call
next(err): If you don't pass the error tonext(), your custom middleware will never be triggered. - Leaking Database Details: Ensure that errors from Mongoose or your database driver are caught and mapped to a generic message before being sent to the client. Never pass raw database errors (which might contain connection strings or table names) directly to
res.json(). - Logging in the Wrong Place: Only log the error once in your central middleware. Avoid logging it in every controller, or your logs will become noisy and repetitive.
FAQ
Q: Should I ever show the stack trace in production? A: Never. It provides a roadmap for attackers to exploit your infrastructure.
Q: What if I need to debug production? A: That is exactly why we log the full error stack to the server console. Use your cloud provider's log viewer to inspect the server-side logs, not the API response.
Q: How do I handle 404 errors with this?
A: 404s are technically errors. You can create a middleware that runs after all your routes to catch requests that didn't match anything, then call next(new Error('Not Found')) to feed them into this handler.
Recap
We've evolved our error handling from a basic catch-all to a professional-grade system. By splitting our output based on NODE_ENV, we balance the need for developer visibility with the absolute requirement for production security.
Up next: We will shift focus toward performance by implementing request caching strategies.
Work with me

Laravel REST API Development
Clean, secure, well-documented Laravel REST APIs — the backend engine for your app, mobile client, or SaaS. Built by an API specialist.

Headless WordPress + Next.js Frontend Development
Keep WordPress for content, get a lightning-fast Next.js frontend. The best of both worlds — familiar editing, modern speed.

