Як обробляти помилки в Express.js?
Обробка Помилок в Express.js
Express має вбудований механізм обробки помилок. Правильна обробка помилок є важливою для уникнення збоїв і повернення змістовних відповідей клієнтам.
Синхронні Помилки
Express автоматично перехоплює помилки, що виникають у синхронних обробниках маршрутів:
app.get('/user/:id', (req, res) => {
if (isNaN(req.params.id)) {
throw new Error('ID must be a number'); // Express catches this
}
res.json({ id: req.params.id });
});Асинхронні Помилки
Для асинхронного коду ви повинні використовувати try/catch або передавати помилки в next():
// Варіант 1: try/catch + next(err)
app.get('/users/:id', async (req, res, next) => {
try {
const user = await User.findById(req.params.id);
if (!user) return res.status(404).json({ error: 'Not found' });
res.json(user);
} catch (err) {
next(err); // передати в обробник помилок
}
});
// Варіант 2: обгортка asyncHandler
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) return res.status(404).json({ error: 'Not found' });
res.json(user);
}));Express 5 (в даний час у RC) автоматично обробляє асинхронні помилки.
Проміжне ПЗ для Обробки Помилок
Обробник помилок має 4 параметри — Express ідентифікує його за аргументом err:
// Повинен бути зареєстрований ОСТАННІМ, після всіх маршрутів
app.use((err, req, res, next) => {
console.error(err.stack);
const status = err.statusCode || err.status || 500;
const message = err.message || 'Internal Server Error';
res.status(status).json({
success: false,
error: message,
// показувати стек тільки в режимі розробки
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});Користувацькі Класи Помилок
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
class NotFoundError extends AppError {
constructor(resource = 'Resource') {
super(`${resource} not found`, 404);
}
}
class ValidationError extends AppError {
constructor(message) {
super(message, 400);
}
}
class UnauthorizedError extends AppError {
constructor() {
super('Unauthorized', 401);
}
}
// Використання в маршрутах
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) throw new NotFoundError('User');
res.json(user);
}));Обробник 404
Перехоплюйте непідходящі маршрути — зареєструйте його після всіх маршрутів, але ДО обробника помилок:
// Після всіх маршрутів
app.use((req, res, next) => {
next(new NotFoundError(`Route ${req.method} ${req.path}`));
});
// Обробник помилок (остання)
app.use((err, req, res, next) => {
res.status(err.statusCode || 500).json({ error: err.message });
});Повна Налаштування Обробки Помилок
const express = require('express');
const app = express();
app.use(express.json());
// Маршрути
app.use('/api/users', usersRouter);
// 404 — повинен йти після всіх маршрутів
app.use((req, res, next) => {
res.status(404).json({ error: 'Route not found' });
});
// Глобальний обробник помилок — повинен бути останнім
app.use((err, req, res, next) => {
const status = err.statusCode || 500;
res.status(status).json({
error: err.message,
...(process.env.NODE_ENV !== 'production' && { stack: err.stack })
});
});
app.listen(3000);Резюме
Реєструйте обробники помилок з 4 параметрами і розміщуйте їх останніми в ланцюзі проміжного ПЗ. Використовуйте next(err) для перенаправлення помилок з асинхронного коду, і створюйте користувацькі класи помилок для перенесення кодів статусу HTTP і контекстних повідомлень.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.