Skip to content
BackendExpressJsNodeJs

4. Express.js Error Handling – Custom Error Handler कैसे सीखे

April 22, 2026 16 min read

नमस्ते दोस्तों! 🙏
स्वागत है The Easy Master पर!

क्या तुमने कभी सोचा है – Express.js app crash क्यों हो जाता है? Aur errors ko properly kaise handle karein?

Jab app production में जाता है, तो errors आना normal है। लेकिन unhandled errors से app crash हो जाता है – user ko “Internal Server Error” dikhta है।

Express.js error handling Hindi में समझना बहुत जरूरी है क्योंकि:

  • Production app mein errors handle karna must है
  • User experience – meaningful error messages
  • Security – stack traces expose nahi karne chahiye
  • Debugging – proper error logging
  • Interview mein pakka error handling questions puche jayenge

Aaj kya seekhoge?

TopicKya Seekhega?
Error Handling Kya Hai?Sync vs Async errors
Custom Error ClassAppError banayenge
Try-CatchAsync error handling
Async Handler WrapperDRY error handling
Custom Error HandlerCentralized error handler
Process ErrorsUncaught exceptions
Production vs DevelopmentEnvironment-based errors

Kya tumhe pata hai?
Express default error handler hai, lekin custom error handler se tum control kar sakte ho ki error kaise dikhe!

तो चलिए शुरू करते हैं – Express.js error handling Hindi सीखने का सफर! 🚀1

1. Error Handling क्या है? – Introduction

Error handling process hai errors ko gracefully handle karne का – bina app crash kare।

Without Error Handling:

Code
// ❌ No error handling – app crashes!
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  // If user not found or DB error → app crashes!
  res.json(user);
});

With Error Handling:

Code
// ✅ With error handling – graceful!
app.get('/api/users/:id', async (req, res, next) => {
  try {
    const user = await db.users.findById(req.params.id);
    
    if (!user) {
      return res.status(404).json({ error: 'User not found' });
    }
    
    res.json(user);
  } catch (error) {
    next(error); // Pass to error handler
  }
});

// Error handler middleware
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({ error: 'Something went wrong!' });
});

Express.js error handling Hindi में हम production-ready error handling system बनाएंगे।

2. Types of Errors – Operational vs Programming

Operational Errors (Predictable):

Code
// Operational errors – can handle gracefully
const errors = {
  'USER_NOT_FOUND': { status: 404, message: 'User not found' },
  'INVALID_INPUT': { status: 400, message: 'Invalid input provided' },
  'UNAUTHORIZED': { status: 401, message: 'Not authorized' },
  'FORBIDDEN': { status: 403, message: 'Access forbidden' },
  'DUPLICATE_EMAIL': { status: 409, message: 'Email already exists' },
  'DATABASE_ERROR': { status: 500, message: 'Database connection failed' }
};

// These are expected – send meaningful response to client

Programming Errors (Bugs):

Code
// Programming errors – bugs in code
// These should be fixed, not handled gracefully

// Example: TypeError, ReferenceError, etc.
// These crash the app – log and restart

Comparison:

FeatureOperational ErrorsProgramming Errors
Predictable✅ Yes❌ No
Can handle✅ Yes❌ No (fix code)
ExamplesDB down, invalid input, not foundTypeError, null reference
Response to clientMeaningful error message500 Internal Error
App restart needed❌ No✅ Yes

3. Synchronous Error Handling

Express automatically synchronous errors catch कर लेता है।

Synchronous Errors:

Code
const express = require('express');
const app = express();

// Express automatically catches sync errors
app.get('/api/sync-error', (req, res) => {
  // This throws error – Express catches it!
  throw new Error('Something went wrong!');
  
  res.json({ message: 'Will never reach here' });
});

// Default error handler will send response

Custom Sync Error Handler:

Code
app.get('/api/users/:id', (req, res, next) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    // Create error and pass to next
    const error = new Error('User not found');
    error.status = 404;
    return next(error);
  }
  
  res.json(user);
});

4. Asynchronous Error Handling – Try-Catch

Express async errors automatically catch nahi karta – हमें try-catch use karna पड़ता है।

Problem (Without Try-Catch):

Code
// ❌ This will crash the app!
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  // If this promise rejects → app crashes!
  res.json(user);
});

Solution (With Try-Catch):

Code
// ✅ Using try-catch
app.get('/api/users/:id', async (req, res, next) => {
  try {
    const user = await db.users.findById(req.params.id);
    
    if (!user) {
      const error = new Error('User not found');
      error.status = 404;
      return next(error);
    }
    
    res.json(user);
  } catch (error) {
    // Pass error to Express error handler
    next(error);
  }
});

Multiple Async Operations:

Code
app.post('/api/orders', async (req, res, next) => {
  try {
    const { userId, productId, quantity } = req.body;
    
    // Validate user
    const user = await db.users.findById(userId);
    if (!user) {
      const error = new Error('User not found');
      error.status = 404;
      return next(error);
    }
    
    // Check product
    const product = await db.products.findById(productId);
    if (!product) {
      const error = new Error('Product not found');
      error.status = 404;
      return next(error);
    }
    
    // Check stock
    if (product.stock < quantity) {
      const error = new Error('Insufficient stock');
      error.status = 400;
      return next(error);
    }
    
    // Create order
    const order = await db.orders.create({
      userId,
      productId,
      quantity,
      total: product.price * quantity
    });
    
    // Update stock
    await db.products.updateStock(productId, -quantity);
    
    res.status(201).json(order);
  } catch (error) {
    next(error);
  }
});

5. Async Handler Wrapper – DRY Pattern

Har route mein try-catch लिखना repetitive है। Async handler wrapper बनाओ – DRY (Don’t Repeat Yourself) principle follow करो।

Basic Async Handler:

Code
// utils/asyncHandler.js
const asyncHandler = (fn) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };
};

module.exports = asyncHandler;

Using Async Handler:

Code
// routes/users.js
const express = require('express');
const asyncHandler = require('../utils/asyncHandler');
const router = express.Router();

// No more try-catch needed!
router.get('/:id', asyncHandler(async (req, res) => {
  const user = await db.users.findById(req.params.id);
  
  if (!user) {
    const error = new Error('User not found');
    error.status = 404;
    throw error;
  }
  
  res.json(user);
}));

router.post('/', asyncHandler(async (req, res) => {
  const { name, email, password } = req.body;
  
  // Validate input
  if (!name || !email || !password) {
    const error = new Error('All fields are required');
    error.status = 400;
    throw error;
  }
  
  // Check duplicate email
  const existingUser = await db.users.findOne({ email });
  if (existingUser) {
    const error = new Error('Email already exists');
    error.status = 409;
    throw error;
  }
  
  const newUser = await db.users.create({ name, email, password });
  res.status(201).json(newUser);
}));

module.exports = router;

Advanced Async Handler with Custom Logic:

Code
// utils/asyncHandler.js
const asyncHandler = (fn, options = {}) => {
  return (req, res, next) => {
    Promise.resolve(fn(req, res, next))
      .catch((err) => {
        // Add custom properties
        err.route = req.route?.path;
        err.method = req.method;
        
        // Log error
        if (options.log !== false) {
          console.error(`[${err.method}] ${err.route} - ${err.message}`);
        }
        
        next(err);
      });
  };
};

module.exports = asyncHandler;

6. Custom Error Class – AppError

AppError class banayenge – sab errors consistent रहेंगे।

AppError Class:

Code
// utils/appError.js
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    this.isOperational = true;
    
    // Capture stack trace
    Error.captureStackTrace(this, this.constructor);
  }
}

module.exports = AppError;

Using AppError:

Code
const AppError = require('../utils/appError');
const asyncHandler = require('../utils/asyncHandler');

// User routes
router.get('/:id', asyncHandler(async (req, res) => {
  const user = await db.users.findById(req.params.id);
  
  if (!user) {
    throw new AppError('User not found', 404);
  }
  
  res.json(user);
}));

router.post('/', asyncHandler(async (req, res) => {
  const { name, email, password } = req.body;
  
  if (!name || !email || !password) {
    throw new AppError('Name, email, and password are required', 400);
  }
  
  const existingUser = await db.users.findOne({ email });
  if (existingUser) {
    throw new AppError('Email already exists', 409);
  }
  
  const newUser = await db.users.create({ name, email, password });
  res.status(201).json(newUser);
}));

router.delete('/:id', asyncHandler(async (req, res) => {
  const user = await db.users.findById(req.params.id);
  
  if (!user) {
    throw new AppError('User not found', 404);
  }
  
  // Only admin can delete
  if (req.user.role !== 'admin' && req.user.id !== user.id) {
    throw new AppError('You are not authorized to delete this user', 403);
  }
  
  await db.users.delete(req.params.id);
  res.status(204).send();
}));

AppError with More Features:

Code
// utils/appError.js (enhanced)
class AppError extends Error {
  constructor(message, statusCode, details = null) {
    super(message);
    
    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
    this.isOperational = true;
    this.details = details;
    this.timestamp = new Date().toISOString();
    
    Error.captureStackTrace(this, this.constructor);
  }
  
  // Static factory methods
  static badRequest(message, details) {
    return new AppError(message, 400, details);
  }
  
  static unauthorized(message = 'Not authorized') {
    return new AppError(message, 401);
  }
  
  static forbidden(message = 'Access forbidden') {
    return new AppError(message, 403);
  }
  
  static notFound(message = 'Resource not found') {
    return new AppError(message, 404);
  }
  
  static conflict(message, details) {
    return new AppError(message, 409, details);
  }
  
  static internal(message = 'Internal server error') {
    return new AppError(message, 500);
  }
}

module.exports = AppError;
Code
// Usage
if (!user) {
  throw AppError.notFound('User not found');
}

if (password.length < 6) {
  throw AppError.badRequest('Password too short', {
    minLength: 6,
    currentLength: password.length
  });
}

if (existingEmail) {
  throw AppError.conflict('Email already registered');
}

7. Custom Error Handler Middleware

Complete Error Handler:

Code
// middleware/errorHandler.js
const AppError = require('../utils/appError');

// Handle specific error types
const handleCastErrorDB = (err) => {
  const message = `Invalid ${err.path}: ${err.value}`;
  return new AppError(message, 400);
};

const handleDuplicateFieldsDB = (err) => {
  const value = err.errmsg.match(/(["'])(\\?.)*?\1/)[0];
  const message = `Duplicate field value: ${value}. Please use another value!`;
  return new AppError(message, 409);
};

const handleValidationErrorDB = (err) => {
  const errors = Object.values(err.errors).map(el => el.message);
  const message = `Invalid input data. ${errors.join('. ')}`;
  return new AppError(message, 400, errors);
};

const handleJWTError = () => new AppError('Invalid token. Please log in again!', 401);
const handleJWTExpiredError = () => new AppError('Your token has expired! Please log in again.', 401);

// Send error in development
const sendErrorDev = (err, res) => {
  res.status(err.statusCode).json({
    success: false,
    error: err,
    message: err.message,
    stack: err.stack,
    details: err.details
  });
};

// Send error in production
const sendErrorProd = (err, res) => {
  // Operational, trusted error: send message to client
  if (err.isOperational) {
    res.status(err.statusCode).json({
      success: false,
      message: err.message,
      ...(err.details && { details: err.details })
    });
  } else {
    // Programming or unknown error: don't leak error details
    console.error('ERROR 💥', err);
    
    res.status(500).json({
      success: false,
      message: 'Something went wrong!'
    });
  }
};

// Main error handler middleware
module.exports = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  err.status = err.status || 'error';
  
  // Development vs Production
  if (process.env.NODE_ENV === 'development') {
    sendErrorDev(err, res);
  } else {
    let error = { ...err };
    error.message = err.message;
    
    // Handle specific database errors
    if (error.name === 'CastError') error = handleCastErrorDB(error);
    if (error.code === 11000) error = handleDuplicateFieldsDB(error);
    if (error.name === 'ValidationError') error = handleValidationErrorDB(error);
    if (error.name === 'JsonWebTokenError') error = handleJWTError();
    if (error.name === 'TokenExpiredError') error = handleJWTExpiredError();
    
    sendErrorProd(error, res);
  }
};

Using Error Handler:

Code
// server.js
const express = require('express');
const AppError = require('./utils/appError');
const errorHandler = require('./middleware/errorHandler');

const app = express();

// Routes
app.use('/api/users', userRoutes);
app.use('/api/products', productRoutes);

// 404 handler (must be before error handler)
app.all('*', (req, res, next) => {
  next(new AppError(`Cannot ${req.method} ${req.originalUrl}`, 404));
});

// Global error handler (must be last)
app.use(errorHandler);

app.listen(3000);

8. 404 Error Handler

404 handler not error middleware है – यह normal middleware है।

404 Handler:

Code
// middleware/notFound.js
const AppError = require('../utils/appError');

const notFoundHandler = (req, res, next) => {
  const error = new AppError(`Cannot ${req.method} ${req.originalUrl}`, 404);
  next(error);
};

module.exports = notFoundHandler;

Using 404 Handler:

Code
// server.js
const notFoundHandler = require('./middleware/notFound');
const errorHandler = require('./middleware/errorHandler');

// All routes
app.use('/api/users', userRoutes);
app.use('/api/products', productRoutes);

// 404 handler – after all routes
app.use(notFoundHandler);

// Error handler – after 404 handler
app.use(errorHandler);

9. Unhandled Rejections and Exceptions

Production mein unhandled errors ko handle karna बहुत जरूरी है।

Global Unhandled Rejection Handler:

Code
// server.js

// Handle uncaught exceptions (synchronous)
process.on('uncaughtException', (err) => {
  console.error('UNCAUGHT EXCEPTION! 💥 Shutting down...');
  console.error(err.name, err.message, err.stack);
  process.exit(1);
});

// Start server
const server = app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

// Handle unhandled promise rejections (async)
process.on('unhandledRejection', (err) => {
  console.error('UNHANDLED REJECTION! 💥 Shutting down...');
  console.error(err.name, err.message, err.stack);
  server.close(() => {
    process.exit(1);
  });
});

// Graceful shutdown for SIGTERM
process.on('SIGTERM', () => {
  console.log('👋 SIGTERM received. Shutting down gracefully...');
  server.close(() => {
    console.log('💥 Process terminated!');
  });
});

10. Production vs Development Error Responses

Environment-based Error Handling:

Code
// config/env.js
const environment = process.env.NODE_ENV || 'development';

const config = {
  development: {
    sendError: (err, res) => {
      res.status(err.statusCode).json({
        success: false,
        message: err.message,
        error: err,
        stack: err.stack,
        details: err.details
      });
    }
  },
  production: {
    sendError: (err, res) => {
      if (err.isOperational) {
        res.status(err.statusCode).json({
          success: false,
          message: err.message,
          ...(err.details && { details: err.details })
        });
      } else {
        console.error('ERROR 💥', err);
        res.status(500).json({
          success: false,
          message: 'Something went wrong!'
        });
      }
    }
  }
};

module.exports = config[environment];

Using Environment Config:

Code
// middleware/errorHandler.js
const envErrorHandler = require('../config/env');

module.exports = (err, req, res, next) => {
  err.statusCode = err.statusCode || 500;
  
  envErrorHandler.sendError(err, res);
};

11. Real-world Error Handling Setup

Complete Production-Ready Setup:

Code
// server.js
const express = require('express');
const mongoose = require('mongoose');
const AppError = require('./utils/appError');
const errorHandler = require('./middleware/errorHandler');
const notFoundHandler = require('./middleware/notFound');

const app = express();

// ============ MIDDLEWARE ============
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// ============ ROUTES ============
app.use('/api/v1/users', userRoutes);
app.use('/api/v1/products', productRoutes);
app.use('/api/v1/orders', orderRoutes);

// ============ HEALTH CHECK ============
app.get('/health', (req, res) => {
  res.status(200).json({
    status: 'OK',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    environment: process.env.NODE_ENV
  });
});

// ============ 404 HANDLER ============
app.use(notFoundHandler);

// ============ GLOBAL ERROR HANDLER ============
app.use(errorHandler);

// ============ START SERVER ============
const PORT = process.env.PORT || 3000;

// Handle uncaught exceptions
process.on('uncaughtException', (err) => {
  console.error('UNCAUGHT EXCEPTION! 💥');
  console.error(err.name, err.message);
  process.exit(1);
});

const server = app.listen(PORT, () => {
  console.log(`🚀 Server running on port ${PORT}`);
  console.log(`📝 Environment: ${process.env.NODE_ENV}`);
});

// Handle unhandled rejections
process.on('unhandledRejection', (err) => {
  console.error('UNHANDLED REJECTION! 💥');
  console.error(err.name, err.message);
  server.close(() => {
    process.exit(1);
  });
});

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('👋 SIGTERM received. Shutting down gracefully...');
  server.close(() => {
    console.log('💥 Process terminated!');
  });
});

Error Response Examples:

Code
// Development Error Response
{
  "success": false,
  "message": "User not found",
  "error": {
    "statusCode": 404,
    "status": "fail",
    "isOperational": true,
    "message": "User not found"
  },
  "stack": "Error: User not found\n    at ...",
  "details": null
}

// Production Error Response (Operational)
{
  "success": false,
  "message": "User not found"
}

// Production Error Response (Programming)
{
  "success": false,
  "message": "Something went wrong!"
}

12. Common Mistakes + Solutions

Mistake 1: Not passing error to next()

Code
// ❌ Error not passed – app hangs
app.get('/api/users/:id', async (req, res) => {
  try {
    const user = await db.findUser(req.params.id);
    res.json(user);
  } catch (error) {
    console.log(error); // Just logs, no response!
  }
});

// ✅ Pass error to next()
app.get('/api/users/:id', async (req, res, next) => {
  try {
    const user = await db.findUser(req.params.id);
    res.json(user);
  } catch (error) {
    next(error);
  }
});

Mistake 2: Sending response after error

Code
// ❌ Sending multiple responses
app.get('/api/users/:id', (req, res, next) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    res.status(404).json({ error: 'Not found' });
    next(new Error('User not found')); // Still sends error response!
  }
  
  res.json(user);
});

// ✅ Either send response OR call next()
app.get('/api/users/:id', (req, res, next) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  
  if (!user) {
    return next(new AppError('User not found', 404));
  }
  
  res.json(user);
});

Mistake 3: Error handler not last

Code
// ❌ Error handler before routes
app.use(errorHandler);
app.use('/api/users', userRoutes); // Routes won't work properly!

// ✅ Error handler LAST
app.use('/api/users', userRoutes);
app.use(errorHandler);

Mistake 4: Not handling async errors

Code
// ❌ No error handling for async route
app.get('/api/users/:id', async (req, res) => {
  const user = await db.findUser(req.params.id); // Can crash!
  res.json(user);
});

// ✅ Use asyncHandler
app.get('/api/users/:id', asyncHandler(async (req, res) => {
  const user = await db.findUser(req.params.id);
  res.json(user);
}));

13. Quick Cheat Sheet

Error Handling Patterns:

PatternCode
Sync errorthrow new Error('message')
Async errornext(error)
Custom errornew AppError(message, statusCode)
Async handlerasyncHandler(fn)
404 handlerapp.use('*', handler)
Error handlerapp.use((err, req, res, next) => {})

Error Handler Order:

Code
// 1. Routes
app.use('/api', routes);

// 2. 404 handler (after routes)
app.use('*', notFoundHandler);

// 3. Error handler (LAST)
app.use(errorHandler);

AppError Usage:

Code
// Basic
throw new AppError('User not found', 404);

// With details
throw new AppError('Validation failed', 400, details);

// Static methods
throw AppError.notFound();
throw AppError.badRequest('Invalid email');
throw AppError.unauthorized();
throw AppError.conflict('Email exists');

14. FAQ

Q1: Express.js error handling Hindi में सबसे important concept kya hai?
next(error) – async errors को error handler तक पहुंचाने का तरीका।

Q2: Sync vs async errors – kya antar hai?
Sync errors Express automatically catch कर लेता है। Async errors को next(error) से pass karna padta है।

Q3: Error handler middleware में 4 parameters kyun?
Express 4 parameters वाले middleware को error handler के रूप में identify करता है।

Q4: 404 handler error handler क्यों नहीं है?
404 handler normal middleware है – यह error नहीं है, sirf “not found” response भेजता है।

Q5: asyncHandler wrapper kyun use karein?
Har route mein try-catch लिखने से बचने के लिए – DRY principle।

Q6: Operational vs Programming errors – kya antar hai?
Operational – predictable, handle कर सकते हो। Programming – bugs, code fix करना पड़ता है।

Q7: Production mein stack trace kyun nahi dikhana chahiye?
Security risk – attackers को app की internal details leak हो सकती हैं।

Q8: unhandledRejection aur uncaughtException mein kya antar hai?
unhandledRejection – async promise rejections। uncaughtException – sync errors।

Q9: AppError class kyun banayein?
Consistent error format, operational flag, aur static factory methods के लिए।

Q10: Error handler last mein kyun lagana chahiye?
Taki सभी routes और middleware ke errors catch हो सकें।

15. Conclusion

बहुत बढ़िया दोस्तों! आज हमने Express.js error handling Hindi को पूरी detail में समझा।

Quick Recap:

ConceptKey Takeaway
Sync ErrorsExpress automatically catches
Async ErrorsUse next(error) or asyncHandler
AppError ClassCustom error with status code
Error Handler4 parameters, must be LAST
404 HandlerNormal middleware, before error handler
ProductionHide stack traces

Mera personal experience:

Error handling samajhne ke baad meri APIs production-ready ho gayi। Pehले errors se app crash ho jata था, ab gracefully handle हो जाता है। Custom error handler se debugging भी easy हो गई।

Tum bhi ye steps follow karo:

  1. ✅ AppError class banao
  2. ✅ asyncHandler wrapper banao
  3. ✅ Custom error handler middleware banao
  4. ✅ 404 handler add karo
  5. ✅ Unhandled rejection/exception handle karo

अब तुम्हारी बारी है!

नीचे comment में बताओ:

  1. तुमने कभी production mein error handling implement kiya है?
  2. तुम्हें कौन सा error type sabse ज्यादा face karta है?
  3. अगला topic क्या चाहिए? (MongoDB with Mongoose? JWT Authentication? File Upload?)

The Easy Master पर बने रहो। Happy Error Handling! 🚀🔧

Resources

Additional Resources

TheEasyMaster

Author at The Easy Master.

Related posts

Leave a Reply

Your email address will not be published. Required fields are marked *