- PVSM.RU - https://www.pvsm.ru -
Знаете это чувство, когда открываешь контроллер в Express проекте, чтобы поправить одну строчку логики, и видишь ЭТО? Бесконечная вложенность, проверки на существование полей, ручной парсинг ошибок от базы данных и, конечно же, его величество try-catch, который занимает 80% файла.
Я тоже через это проходил. В каждом новом микросервисе я копипастил одни и те же функции обработки ошибок. В одном проекте я ловил ошибки Mongoose через err.name === 'ValidationError', в другом — через instanceof. Где-то мы отдавали { error: "message" }, где-то { status: "fail", msg: "..." }.
В какой-то момент мне это надоело. Мне захотелось инструмент, который я могу просто подключить одной строкой, и он сам поймет, что "E11000" от Mongo — это 409 Conflict, а ошибка Zod — это 400 Bad Request. При этом я не хотел тянуть в проект тяжелые зависимости.
Так родилась библиотека ds-express-errors. Сегодня я расскажу, зачем я ее написал и почему она может сэкономить вам кучу нервов.
Давайте посмотрим на типичный код джуна (да и не только), который пишет регистрацию юзера:
// Типичный "страшный сон"
app.post('/register', async (req, res) => {
try {
const { email, password } = req.body;
// 1. Ручная валидация
if (!email) return res.status(400).json({ error: 'Email нужен' });
// 2. Сама логика
const user = await User.create({ email, password });
res.status(201).json(user);
} catch (err) {
// 3. Ад обработки ошибок
console.error(err); // Ну хоть в консоль плюнем
if (err.code === 11000) {
return res.status(409).json({ error: 'Такой email уже есть' });
}
if (err.name === 'ValidationError') {
// Тут обычно пишут map, чтобы достать сообщения
return res.status(400).json({ error: 'Ошибка валидации...' });
}
res.status(500).json({ error: 'Всё упало' });
}
});
Это больно читать. Это больно поддерживать. И самое главное — это нужно писать в каждом контроллере.
Я хотел, чтобы код выглядел так: «Пробуем сделать действие. Если не вышло — кидаем понятную ошибку. Если база ругается — пусть библиотека сама разбирается».
Вот тот же самый код с использованием моей либы:
const { asyncHandler, Errors } = require('ds-express-errors');
// Оборачиваем в asyncHandler, чтобы забыть про try-catch
app.post('/register', asyncHandler(async (req, res) => {
const { email, password } = req.body;
// Просто делаем запрос. Если email занят, либа сама перехватит ошибку Mongoose,
// поймет, что это дубликат, и отдаст 400-й код с понятным сообщением.
const user = await User.create({ email, password });
// Если нужно кинуть свою ошибку:
if (!user) throw Errors.BadRequest('Не удалось создать');
res.status(201).json(user);
}));
Всё. Никакого шума. Только бизнес-логика.
Я написал централизованный errorHandler middleware, который вешается в конце цепочки Express.
Главная фишка — автоматические мапперы. Библиотека смотрит на прилетевшую ошибку и пытается понять, откуда она:
Mongoose: Ловит ValidationError, CastError (кривой ID) и дубликаты (code 11000).
Prisma: Если вы на темной стороне (шучу), библиотека парсит коды P2002, P2003 и другие.
Валидаторы: Поддерживаются Zod и Joi. Ошибки форматируются в одну строчку, типа: email: Invalid email; age: Too small.
JWT: Просроченные или левые токены сразу дают 401 Unauthorized.
Вам не нужно писать if (err instanceof ZodError). Оно просто работает.
Когда вы деплоите новую версию, старый под получает сигнал SIGTERM. Если ваше приложение в этот момент просто умрет (как делает process.exit), то все юзеры, у которых прямо сейчас грузился файл или шла транзакция, получат обрыв соединения.
Я встроил в либу утилиту для Graceful Shutdown. Она:
Перестает принимать новые запросы.
Ждет, пока завершатся текущие (через server.close).
Дает вам хук onShutdown, чтобы закрыть коннекты к БД.
Если всё зависло — убивает процесс по таймауту, чтобы не висеть зомби.
Использование элементарное:
const { initGlobalHandlers, gracefulHttpClose } = require('ds-express-errors');
const server = app.listen(3000);
initGlobalHandlers({
closeServer: gracefulHttpClose(server),
onShutdown: async () => {
await mongoose.disconnect(); // Чистим за собой
console.log('БД закрыта, уходим в закат');
}
});
Теперь ваши графики ошибок 5xx при деплое будут чистыми.
Это мой пунктик. Я ненавижу, когда ставишь маленькую либу, а она тянет за собой половину npm.
В package.json у ds-express-errors ровно 0 (ноль) зависимостей в dependencies. Она весит копейки, быстро ставится и не создает дыр в безопасности через third-party пакеты.
При этом она написана на JS, но внутри лежат .d.ts файлы, так что автокомплит в VS Code и поддержка TypeScript работают из коробки.
Библиотека доступна в npm.
npm install ds-express-errors
В коде (обычно в app.js или index.js):
const { errorHandler } = require('ds-express-errors');
// ... ваши роуты ...
// Важно: подключаем в самом конце, ПОСЛЕ всех роутов
app.use(errorHandler);
Я писал эту библиотеку в первую очередь для себя, чтобы перестать копипастить код между проектами. Но сейчас она выросла в полноценный инструмент, который закрывает много потребностей при работе с ошибками в Node.js API.
Если вам тоже надоело писать бойлерплейт — попробуйте.
🔗 Доки и сайт: ds-express-errors.dev [1] 🐙 GitHub: ds-express-errors [2]
Буду рад любым звездам, оценкам и issues. Пишите в комменты, если есть идеи, что еще добавить (сейчас думаю над поддержкой TypeORM).
Всем чистого кода и зеленых тестов! 🖖
Автор: Dianka05
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/440482
Ссылки в тексте:
[1] ds-express-errors.dev: http://ds-express-errors.dev
[2] ds-express-errors: https://github.com/Dianka05/ds-express-errors
[3] Источник: https://habr.com/ru/articles/981456/?utm_source=habrahabr&utm_medium=rss&utm_campaign=981456
Нажмите здесь для печати.