Best way to handle the error in async node
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Error handling in asynchronous Node.js code should be explicit, consistent, and layered. Without a strategy, rejected promises and callback errors can leak into unhandled exceptions and crash processes unexpectedly. The best practice is to standardize on async and await where possible, centralize request-level handling, and keep process-level handlers as a last safety net.
Core Pattern with async and await
Use try and catch around awaited operations and throw domain-specific errors when needed.
Adding context before rethrowing helps logs and debugging.
Express Route Error Wrapper
In Express, wrap async route handlers so rejections reach centralized middleware.
This avoids repetitive try and catch blocks in every route.
Promise Chains and Error Propagation
Legacy code may still use .then and .catch. Keep chains flat and return promises properly.
Missing return in intermediate .then callbacks is a common source of swallowed errors.
Callback-Based APIs
Some Node APIs still use error-first callbacks. Normalize them with util.promisify.
Standardizing async style simplifies error paths.
Operational Versus Programmer Errors
Treat errors differently by category:
- Operational errors: network timeouts, missing records, dependency failures.
- Programmer errors: null dereference, type bugs, invariant violations.
Operational errors should be handled and returned gracefully. Programmer errors often justify process restart after logging because application state may be corrupted.
Process-Level Safety Handlers
Keep global handlers as fallback, not primary logic.
These handlers capture failures, but route-level and service-level handling should still be your first line of defense.
Structured Logging and Correlation IDs
Error handling is only useful if diagnostics are searchable. Include request id or job id with logs.
Structured logs make incident response faster in distributed systems.
Retry and Timeout Boundaries
Not every async error should retry. Define explicit retry policy and timeout limits.
Bounded retries with backoff improve reliability without creating retry storms.
Common Pitfalls
- Mixing callback, promise, and async styles inconsistently. Fix by standardizing on
asyncandawaitwhere possible. - Handling errors only at process level. Fix by catching errors at request and service boundaries first.
- Swallowing promise rejections without logging context. Fix by logging structured details and rethrowing intentionally.
- Returning generic 500 for all cases. Fix by mapping domain errors to appropriate status codes.
- Retrying every failure blindly. Fix by applying targeted retry logic with limits and timeouts.
Summary
- Use layered error handling: function, route, then process fallback.
- Prefer
asyncandawaitwith explicittryandcatchblocks. - Centralize HTTP framework error middleware for consistency.
- Distinguish operational errors from programmer errors.
- Add structured logging and bounded retries for production resilience.

