intermediate
Error handling
Отделяйте operational errors от programmer bugs, мапьте errors в responses, сохраняйте causes и избегайте unhandled rejections.
Разделяйте operational errors (ожидаемые сбои — validation, 404, таймаут upstream) и programmer errors (баги — null, нарушение инварианта). Operational мапятся в безопасный HTTP-ответ; programmer после лога могут завершить процесс, чтобы supervisor перезапустил испорченный инстанс.
class AppError extends Error {
constructor(message, { status = 500, code = 'INTERNAL', cause }) {
super(message, { cause });
this.status = status;
this.code = code;
this.isOperational = true;
}
}
process.on('unhandledRejection', (reason) => {
logger.error({ reason }, 'unhandled rejection');
shutdown(1);
});
Async-ошибки всегда в `next(err)` или аналог фреймворка. Сохраняйте цепочку `cause`; из клиентского JSON убирайте внутренние детали.
На интервью: operational vs programmer; форма централизованного error middleware; опасность «проглоченных» rejection.
Типовые ошибки: пустой `catch {}`; 500 со stack trace клиенту; разные формы ошибок по маршрутам.
Чеклист:
- Структурированные ошибки приложения.
- Один mapper ответов для API.
- Мониторинг unhandledRejection/uncaughtException.
- Stack только на сервере.