Обработка ошибок

Чтобы один кривой запрос не уронил весь сервер — учимся ловить и возвращать ошибки.

Почему это критично

Сервер работает долго и обслуживает многих. Если ошибка в одном запросе не поймана, она может уронить весь процесс — и все клиенты получат отказ. Поэтому ошибки нужно ловить и аккуратно превращать в HTTP-ответы.

Валидация входных данных

Самый частый источник ошибок — некорректный ввод. Проверяйте данные ДО работы с ними и возвращайте 400 Bad Request с понятным сообщением:

app.post("/tasks", (req, res) => {
  const { title } = req.body;
  if (!title || typeof title !== "string") {
    return res.status(400).json({ error: "Поле title обязательно" });
  }
  // ... создаём задачу
  res.status(201).json({ title });
});

Логика проверки — чистый JS, её легко протестировать:

function validateTask(body) {
  const errors = [];
  if (!body.title) errors.push("title обязателен");
  if (body.title && body.title.length > 100) errors.push("title слишком длинный");
  if (body.priority && ![1, 2, 3].includes(body.priority)) errors.push("priority вне 1..3");
  return errors;
}

console.log(validateTask({ title: "Помыть посуду", priority: 2 }));
console.log(validateTask({ priority: 5 }));

Вывод:

[]
[ 'title обязателен', 'priority вне 1..3' ]

try/catch в async-маршрутах

Когда внутри маршрута есть await (запрос к БД, чтение файла), оборачивайте его в try/catch, чтобы поймать сбой и ответить ошибкой, а не «уронить» запрос:

app.get("/tasks/:id", async (req, res) => {
  try {
    const task = await db.findTask(req.params.id);
    if (!task) return res.status(404).json({ error: "Не найдено" });
    res.json(task);
  } catch (err) {
    res.status(500).json({ error: "Внутренняя ошибка сервера" });
  }
});

Error-handling middleware

В Express есть особый middleware для ошибок — у него четыре аргумента: (err, req, res, next). Express узнаёт его по числу аргументов и направляет туда все ошибки. Подключают его в самом конце:

// обычные маршруты выше...

// централизованный обработчик ошибок — в самом конце
app.use((err, req, res, next) => {
  console.error(err.message);
  res.status(500).json({ error: "Что-то пошло не так" });
});

Сюда попадут ошибки, переданные через next(err). Это позволяет не повторять обработку в каждом маршруте, а собрать её в одном месте.

Подходящие статус-коды

КодКогда
400неверные данные от клиента
401 / 403не авторизован / нет прав
404ресурс не найден
500ошибка на сервере

Правило: 4xx — виноват клиент (неверный запрос), 5xx — виноват сервер.

Итог

  • Валидируйте вход и возвращайте 400 с понятным сообщением.
  • Оборачивайте await в маршрутах в try/catch.
  • Error-middleware с 4 аргументами (err, req, res, next) ставят в конце.
  • 4xx — ошибка клиента, 5xx — ошибка сервера.
Проверьте себя
1. По какому признаку Express узнаёт обработчик ошибок?
AПо имени функции
BПо четырём аргументам (err, req, res, next)
CПо расположению в файле
DПо специальному декоратору
2. Какой статус вернуть на некорректные данные от клиента?
A200
B400
C404
D500
3. Зачем оборачивать await в маршруте в try/catch?
AЧтобы ускорить запрос
BЧтобы поймать сбой и вернуть ошибку, а не уронить запрос
CЭто требование npm
DЧтобы включить логи
Поддержать проект