← Назад к вопросам

Как реализовать глобальный error-handling middleware в Express?

2.0 Middle🔥 201 комментариев
#Node.js и JavaScript#Фреймворки и библиотеки

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Глобальный Error-Handling Middleware в Express

Error-handling middleware — это критически важный компонент любого production-приложения на Express. Это специальный middleware, который автоматически ловит все ошибки и обрабатывает их централизованно.

Основной принцип

В Express error-handling middleware отличается от обычного middleware наличием 4 параметров: (err, req, res, next). Это говорит Express, что это именно error handler. Важно: error-handling middleware должен быть определён последним, после всех остальных middleware и route handlers.

Базовая реализация

// Простой глобальный error handler
app.use((err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const message = err.message || "Internal Server Error";

  res.status(statusCode).json({
    error: {
      message: message,
      status: statusCode,
    },
  });
});

Продвинутая реализация с классами ошибок

// Класс для кастомных ошибок приложения
class AppError extends Error {
  constructor(message, statusCode) {
    super(message);
    this.statusCode = statusCode;
    Error.captureStackTrace(this, this.constructor);
  }
}

// Error handler middleware
const errorHandler = (err, req, res, next) => {
  // Логирование ошибки (в продакшене отправляй в сервис мониторинга)
  console.error(err);

  // Валидация данных
  if (err.name === "ValidationError") {
    return res.status(400).json({
      error: {
        message: "Validation failed",
        details: err.details,
      },
    });
  }

  // JWT ошибки
  if (err.name === "JsonWebTokenError") {
    return res.status(401).json({
      error: { message: "Invalid token" },
    });
  }

  // Наш кастомный AppError
  if (err instanceof AppError) {
    return res.status(err.statusCode).json({
      error: { message: err.message },
    });
  }

  // Неожиданная ошибка
  res.status(500).json({
    error: { message: "Internal Server Error" },
  });
};

app.use(errorHandler);

Асинхронные операции

Главная сложность — ловля ошибок из асинхронных функций. Для этого используй обёртку:

// Обёртка для ловли ошибок в async route handlers
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

// Использование
app.get("/users/:id", asyncHandler(async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) {
    throw new AppError("User not found", 404);
  }
  res.json(user);
}));

Production-ready решение

const errorHandler = (err, req, res, next) => {
  const isDevelopment = process.env.NODE_ENV === "development";

  // Логирование + мониторинг (Sentry, DataDog и т.д.)
  if (!isDevelopment) {
    logger.error({
      message: err.message,
      stack: err.stack,
      path: req.path,
      method: req.method,
    });
  }

  const statusCode = err.statusCode || 500;
  const message = isDevelopment ? err.message : "Internal Server Error";

  res.status(statusCode).json({
    error: {
      message,
      ...(isDevelopment && { stack: err.stack }),
    },
  });
};

Ключевые моменты

  • Порядок имеет значение: error handler должен быть последним app.use()
  • Все ошибки должны быть пойманы: используй asyncHandler для асинхронного кода
  • Никогда не отправляй стек-трейс в продакшене: это уязвимость
  • Логирование: обязательно логируй в продакшене для отладки инцидентов
  • HTTP статусы: используй правильные коды (400, 401, 403, 404, 500)