logo
Published on

Error handling Technique in NodeJs

Authors
  • avatar
    Name
    Ganesh Negi
    Twitter
Error handling in Nodejs

Error handling Technique in NodeJs.

Error handling is crucial for building reliable and maintainable Node.js applications. A robust error-handling strategy ensures stability, prevents crashes, and improves the user experience. This guide covers essential techniques and best practices for handling errors effectively in Node.js.

Key Error Handling Strategies

Middleware for Centralized Error Handling

Using Built-in Error Classes

Third-party Error Handling Libraries

Error Propagation in Synchronous & Asynchronous Code

Graceful Shutdown Techniques

Error Monitoring & Reporting

Circuit Breaker Implementation

Managing Backpressure in Streams

Automatic Retries for Reliability

Effective Logging & Analytics

1. Centralized Error Middleware in Express

Express allows defining a middleware function to handle errors centrally, improving code maintainability.


app.use((err, req, res, next) => {
  res.status(err.status || 500).json({ error: err.message });
});

Example: Handling Errors in Routes

app.get('/users/:id', (req, res, next) => {
  if (req.params.id === 'admin') {
    return next({ status: 403, message: 'Access denied' });
  }
  res.send('User access granted');
});

2. Utilizing Built-in Error Classes

Node.js provides standard error classes like Error, TypeError, and RangeError. Custom error classes can also be created.


app.get('/users/:id', (req, res, next) => {
  if (req.params.id === 'admin') {
    return next({ status: 403, message: 'Access denied' });
  }
  res.send('User access granted');
});

3. Third-party Error Handling Libraries

Libraries such as @hapi/boom provide structured error management.

const boom = require('@hapi/boom');
app.get('/users/:id', (req, res, next) => {
  return next(boom.forbidden('Access denied'));
});

4. Proper Error Propagation

Errors should be propagated correctly to prevent unhandled exceptions.

Synchronous Code

function divide(a, b) {
  if (b === 0) {
    throw new Error('Cannot divide by zero');
  }
  return a / b;
}

Asynchronous Code

async function fetchData() {
  throw new Error('Data fetch failed');
}

fetchData().catch(console.error);

5. Implementing Graceful Shutdown

Ensure clean application exits by handling shutdown signals and closing resources.


process.on('SIGINT', async () => {
  console.log('Closing resources...');
  process.exit(0);
});

6. Error Monitoring & Reporting

Use monitoring tools like Sentry or Rollbar to track errors in production.

const Sentry = require('@sentry/node');
Sentry.init({ dsn: 'YOUR_DSN_HERE' });
app.use((err, req, res, next) => {
  Sentry.captureException(err);
  res.status(500).json({ error: 'Something went wrong' });
});

7. Implementing Circuit Breaker Pattern

Circuit breakers prevent cascading failures by temporarily halting requests after repeated failures.

const CircuitBreaker = require('opossum');
const breaker = new CircuitBreaker(fetchData, { timeout: 3000, errorThresholdPercentage: 50 })

8. Managing Backpressure in Streams

Use proper flow control mechanisms to handle large data streams efficiently.


const fs = require('fs');
const readable = fs.createReadStream('file.txt');
const writable = fs.createWriteStream('output.txt');

readable.pipe(writable);

9. Implementing Automatic Retries

Retry failed operations automatically for better resilience.


const retry = require('async-retry');
retry(() => fetchSomeData(), { retries: 3 }).catch(console.error);

10. Structured Logging & Analytics

Use a logging library like Winston for structured error tracking.

const winston = require('winston');
const logger = winston.createLogger({
  level: 'error',
  transports: [new winston.transports.Console()]
});

Conclusion

A solid error-handling strategy improves the robustness of Node.js applications. By using middleware, error propagation techniques, monitoring tools, and structured logging, developers can create more reliable and maintainable applications.