← Назад к вопросам
Что такое очереди сообщений и зачем они нужны в 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 })
}
});
};