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

Что такое очереди сообщений и зачем они нужны в Node.js приложениях?

2.3 Middle🔥 281 комментариев
#Архитектура и паттерны#Брокеры сообщений и очереди

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

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

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

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

Глобальный error-handling middleware — это функция, которая ловит все ошибки в Express приложении и обрабатывает их в одном месте.

Базовая структура

import express, { ErrorRequestHandler } from 'express';

const app = express();

// 1. Все остальные middleware и роуты

// 2. Error handling middleware (ДОЛЖНО быть в конце!)
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  console.error('Error:', err);
  res.status(500).json({ error: err.message });
};

app.use(errorHandler);

Полный пример с типизацией

import express, { ErrorRequestHandler, Request, Response, NextFunction } from 'express';

interface ApiError extends Error {
  status?: number;
}

const app = express();

// Роуты
app.get('/users/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    if (!req.params.id) {
      const error = new Error('ID required');
      (error as ApiError).status = 400;
      throw error; // ← Передаёт в error handler
    }
    
    const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
    res.json(user);
  } catch (error) {
    next(error); // ← Передаёт в error handler
  }
});

// Ловим 404
app.use((req, res) => {
  res.status(404).json({ error: 'Not Found' });
});

// Глобальный error handler (ОБЯЗАТЕЛЬНО в конце!)
const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  const status = (err as ApiError).status || 500;
  const message = err.message || 'Internal Server Error';
  
  console.error(`[${new Date().toISOString()}] Error ${status}: ${message}`);
  
  res.status(status).json({
    error: {
      message,
      status,
      timestamp: new Date().toISOString()
    }
  });
};

app.use(errorHandler);

С классом ошибок

class AppError extends Error {
  constructor(
    public message: string,
    public status: number = 500,
    public details?: any
  ) {
    super(message);
    this.name = this.constructor.name;
  }
}

class ValidationError extends AppError {
  constructor(message: string, details?: any) {
    super(message, 400, details);
  }
}

class NotFoundError extends AppError {
  constructor(message: string = 'Not Found') {
    super(message, 404);
  }
}

// Использование
app.get('/users/:id', async (req, res, next) => {
  try {
    if (!req.params.id) {
      throw new ValidationError('ID required');
    }
    
    const user = await db.query('SELECT * FROM users WHERE id = $1', [req.params.id]);
    if (!user) {
      throw new NotFoundError('User not found');
    }
    
    res.json(user);
  } catch (error) {
    next(error);
  }
});

// Error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  if (err instanceof AppError) {
    return res.status(err.status).json({
      error: err.message,
      details: err.details
    });
  }
  
  // Unknown error
  res.status(500).json({ error: 'Internal Server Error' });
});

С логированием

import winston from 'winston';

const logger = winston.createLogger({
  format: winston.format.json(),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' })
  ]
});

const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  const status = (err as ApiError).status || 500;
  const requestId = req.headers['x-request-id'] || 'unknown';
  
  // Логируем с контекстом
  logger.error({
    message: err.message,
    status,
    requestId,
    method: req.method,
    path: req.path,
    stack: err.stack,
    userId: (req as any).user?.id
  });
  
  res.status(status).json({
    error: {
      message: status === 500 ? 'Internal Server Error' : err.message,
      requestId
    }
  });
};

app.use(errorHandler);

Асинхронные ошибки

// ✗ ПЛОХО: Забыли await
app.get('/users', async (req, res, next) => {
  db.query('SELECT * FROM users'); // Ошибка не поймается!
  res.json({});
});

// ✓ ХОРОШО
app.get('/users', async (req, res, next) => {
  try {
    const users = await db.query('SELECT * FROM users');
    res.json(users);
  } catch (err) {
    next(err);
  }
});

// ✓ С wrapper функцией
const asyncHandler = (fn: Function) => (req: Request, res: Response, next: NextFunction) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

app.get('/users', asyncHandler(async (req, res) => {
  const users = await db.query('SELECT * FROM users');
  res.json(users);
}));

Production-ready error handler

const errorHandler: ErrorRequestHandler = (err, req, res, next) => {
  const status = (err as any).status || 500;
  const isDev = process.env.NODE_ENV === 'development';
  
  // Логирование
  console.error({
    timestamp: new Date().toISOString(),
    status,
    message: err.message,
    path: req.path,
    method: req.method,
    stack: isDev ? err.stack : undefined
  });
  
  // Отправляем в Sentry
  if (status >= 500) {
    Sentry.captureException(err);
  }
  
  // Ответ
  res.status(status).json({
    error: {
      message: isDev ? err.message : 'Internal Server Error',
      status,
      ...(isDev && { stack: err.stack })
    }
  });
};
Что такое очереди сообщений и зачем они нужны в Node.js приложениях? | PrepBro