Первый сервер на Express: частые ошибки новичков
Разбираем ошибки, на которые натыкаются все, кто поднимает первый сервер на Express 5: от «Cannot GET /» до утечки токенов в репозиторий. С готовым минимальным сервером и разделом «если не получилось».

За полчаса вы поднимете рабочий HTTP-сервер на Express 5, который отдаёт JSON, читает тело POST-запроса и не падает при первой же ошибке. По ходу разберём шесть мест, где спотыкается большинство новичков: порядок middleware, отсутствие обработки ошибок, забытый express.json(), порт, занятый другим процессом, и токены, случайно уехавшие в Git.
Материал написан под Express 5.x (стабильный релиз вышел осенью 2024 года) и Node.js 20 LTS или новее. В пятой версии часть привычных по гайдам вещей поменялась — на этом ниже отдельно остановимся.
Что нужно до старта
Не начинайте установку, пока не проверите три вещи. Иначе застрянете не на коде, а на окружении.
- Node.js версии 20 или выше. Проверьте командой
node -v. Express 5 официально требует Node 18+, но 20 LTS — безопасный минимум. - npm или другой пакетный менеджер. Идёт вместе с Node. Проверка:
npm -v. - Пустая папка проекта и терминал, открытый именно в ней. Половина проблем «файл не находится» — это запуск не из той директории.
Минимальный рабочий сервер
Инициализируйте проект и поставьте Express:
mkdir my-server && cd my-server
npm init -y
npm install expressЧтобы использовать синтаксис import, добавьте в package.json строку "type": "module". Без неё Node ждёт require и выдаст SyntaxError: Cannot use import statement outside a module.
Создайте файл server.js:
import express from 'express';
const app = express();
const PORT = process.env.PORT || 3000;
// Разбор JSON в теле запроса. Без этой строки req.body будет undefined
app.use(express.json());
// Простой GET
app.get('/', (req, res) => {
res.json({ message: 'Сервер работает' });
});
// POST, который читает тело запроса
app.post('/echo', (req, res) => {
res.json({ received: req.body });
});
app.listen(PORT, () => {
console.log(`Слушаю на http://localhost:${PORT}`);
});Запустите: node server.js. В терминале должна появиться строка Слушаю на http://localhost:3000. Откройте в браузере http://localhost:3000 — увидите JSON {"message":"Сервер работает"}. Если так — база работает, дальше разбираем, где всё ломается.
Ошибка 1. «Cannot GET /» — маршрута нет
Вы открываете браузер и вместо ответа видите Cannot GET /. Это не сбой сервера, а его штатный ответ: запрошенный путь не совпал ни с одним маршрутом. Частая причина — обращаетесь на /api, а объявлен только /. Или метод не тот: браузер по адресной строке всегда шлёт GET, а вы объявили app.post.
Проверьте, что путь в app.get('/путь', ...) буква в букву совпадает с тем, что в адресной строке, и метод соответствует. POST из браузерной строки не проверить — используйте curl или расширение для запросов.
Ошибка 2. req.body равен undefined
Вы шлёте POST с JSON, а в обработчике req.body оказывается undefined — и код падает при обращении к его полям. Причина почти всегда одна: не подключён парсер тела. Строка app.use(express.json()) должна стоять до маршрутов, которые читают тело.
Второй нюанс — заголовок запроса. express.json() разбирает тело, только если клиент прислал Content-Type: application/json. Проверьте отправку через curl:
curl -X POST http://localhost:3000/echo \
-H "Content-Type: application/json" \
-d '{"name":"test"}'В ответ должно прийти {"received":{"name":"test"}}. Если вернулось {"received":{}} — забыли заголовок или отправили не JSON.
Ошибка 3. Порядок middleware
Express выполняет middleware и маршруты в том порядке, в котором они объявлены. Это не декларация, а последовательность. Если app.use(express.json()) написать после обработчика POST, парсер к этому обработчику не применится, и req.body снова будет пустым — хотя строка в файле есть.
Express — это не конфигурация, а конвейер. Каждый запрос идёт по нему сверху вниз, и порядок строк в файле — это порядок обработки. Переставили местами — изменили поведение.
Правило: сначала глобальные middleware (парсеры, логирование, CORS), потом маршруты, в самом конце — обработчик 404 и обработчик ошибок.
Ошибка 4. Нет обработки ошибок — сервер падает целиком
Новичок ловит ошибку в одном обработчике, а падает весь процесс, и остальные пользователи получают обрыв соединения. В синхронном коде Express 5 сам ловит брошенные исключения. А вот с асинхронным есть важное изменение.
Что поменялось в Express 5
В Express 4 промис, отклонённый внутри async-обработчика, никуда не передавался — приходилось оборачивать всё в try/catch вручную. В Express 5 отклонённые промисы из async-функций автоматически передаются в обработчик ошибок. Это одно из главных отличий версии, и старые гайды по «обёрткам для async» под Express 5 уже избыточны.
Обработчик ошибок объявляется функцией с четырьмя аргументами и ставится последним:
app.get('/data', async (req, res) => {
const data = await loadData(); // если промис упадёт, Express 5 передаст ошибку дальше
res.json(data);
});
// 404 — если ни один маршрут не подошёл
app.use((req, res) => {
res.status(404).json({ error: 'Не найдено' });
});
// Обработчик ошибок — ровно 4 аргумента, последним в файле
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Внутренняя ошибка сервера' });
});Признак, что обработчик ошибок не сработал: в терминале длинный стектрейс с UnhandledPromiseRejection, а клиенту вообще ничего не пришло. Значит, либо у функции не четыре аргумента (Express считает её обычным маршрутом), либо она объявлена не последней.
Ошибка 5. Порт уже занят
При запуске падает с Error: listen EADDRINUSE: address already in use :::3000. Порт 3000 держит другой процесс — часто это ваш же прошлый запуск, который не закрылся. Варианты:
- Закройте старый терминал, где сервер ещё крутится, или остановите его через Ctrl+C.
- Найдите и завершите процесс. На macOS/Linux:
lsof -i :3000покажет PID, дальшеkill PID. На Windows:netstat -ano | findstr :3000, затемtaskkill /PID номер /F. - Запустите на другом порту:
PORT=3001 node server.js— в коде вышеprocess.env.PORTэто учитывает.
Ошибка 6. Токены и пароли в коде
Самая дорогая ошибка — не техническая. Ключ к базе или API-токен, вписанный прямо в server.js, уезжает в Git при первом коммите и остаётся в истории навсегда, даже если вы потом его удалите. Публичный репозиторий с живым токеном находят автоматические сканеры за минуты.
Держите секреты в переменных окружения, а не в коде:
// Правильно: значение приходит из окружения
const apiKey = process.env.API_KEY;
// Неправильно: секрет в исходнике
// const apiKey = 'YOUR_API_KEY';Создайте файл .env для локальных значений и сразу добавьте его в .gitignore. Для загрузки в Node 20+ можно использовать встроенный флаг --env-file: node --env-file=.env server.js. Уточните доступность флага в документации вашей версии Node — он появился недавно и в старых релизах отсутствует.
Сводка: симптом и причина
| Что видите | Причина | Что делать |
|---|---|---|
| Cannot GET /path | Нет маршрута или не тот метод | Сверить путь и метод (GET/POST) |
| req.body — undefined | Нет express.json() или он после маршрута | Поставить парсер до маршрутов, слать Content-Type |
| EADDRINUSE | Порт занят другим процессом | Закрыть старый запуск или сменить порт |
| UnhandledPromiseRejection | Нет обработчика ошибок или у него не 4 аргумента | Добавить (err, req, res, next) последним |
| Cannot use import statement | Нет "type": "module" в package.json | Добавить поле или использовать require |
Что делать дальше
Когда базовый сервер устойчив, следующий шаг — не добавлять сразу базу и авторизацию, а привести в порядок процесс разработки. Поставьте автоперезапуск при изменении файлов: в Node 20+ есть встроенный флаг --watch (node --watch server.js), отдельный nodemon больше не обязателен. Дальше вынесите маршруты в отдельные модули через express.Router(), чтобы server.js не разросся. Логирование запросов, CORS и валидацию тела добавляйте по мере надобности — каждый пункт отдельная тема, здесь мы разбирали только первый рабочий сервер и типовые грабли на старте.
Помощник по реферату: Готовый текст: введение, разделы, заключение, литература
Спросить за 15 ₽Источники: Express — официальная документация (Getting started, Migrating to Express 5), Node.js Documentation — Command-line API (--watch, --env-file)
Частые вопросы
У меня Express 4, а не 5 — эти советы подойдут?
Сервер запустился, но браузер пишет «Cannot GET /». Что не так?
Почему req.body пустой, хотя express.json() я подключил?
А если сервер вообще не запускается и пишет EADDRINUSE?
Нужно ли ставить nodemon для автоперезапуска?
Я случайно закоммитил токен в Git — достаточно его удалить из файла?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.