Маршрутизация в Express: пути, параметры, роутеры

Содержание

О проверке кода. Примеры не запускались в этой сессии (по договорённости — фокус на контенте, а не прогонах). Говорим прямо, а не ставим бейдж «проверено».

Маршрутизация — то, как Express решает, какой код запустить на конкретный запрос. По мере роста приложения грамотная организация маршрутов становится критичной. Разберём от простого к структурированному.

Методы и пути

app.get('/products', handler);      // получить список
app.post('/products', handler);     // создать
app.put('/products/:id', handler);  // заменить целиком
app.patch('/products/:id', handler);// изменить частично
app.delete('/products/:id', handler);// удалить

Маршрут — это пара «метод HTTP + путь». Один и тот же путь /products/:id с разными методами (GET, PUT, DELETE) — это разные маршруты с разной логикой. Так строится REST API: метод выражает действие, путь — ресурс.

Параметры пути и строка запроса

Два способа передать данные в URL — их путают:

// параметр пути — /users/:id
app.get('/users/:id', (req, res) => {
  req.params.id;        // '42' из /users/42 — обязательная часть маршрута
});

// несколько параметров
app.get('/users/:userId/orders/:orderId', (req, res) => {
  req.params.userId;    // '42'
  req.params.orderId;   // '7'
});

// строка запроса — ?sort=name&page=2
app.get('/products', (req, res) => {
  req.query.sort;       // 'name' — необязательный фильтр/сортировка
  req.query.page;       // '2'
});
Параметр пути против строки запроса

Параметр пути /:id

req.params

  • Часть самого маршрута
  • Обязателен — определяет ресурс
  • /users/42 → id = 42
  • Для идентификации: какой пользователь

Строка запроса ?x=y

req.query

  • После знака вопроса
  • Обычно необязательна
  • ?sort=name&page=2
  • Для фильтров, сортировки, пагинации

Параметр пути идентифицирует ресурс (какой именно пользователь) и обязателен. Строка запроса задаёт опции (как отсортировать, какая страница) и обычно необязательна. Значения всегда строки — число страницы приводите через Number. Разбор строки запроса Express берёт на себя (под капотом — URLSearchParams).

Разбиение на модули: express.Router

Держать все маршруты в одном файле — путь к хаосу. Router разбивает их на модули:

// routes/users.js — отдельный файл маршрутов пользователей
const router = express.Router();

router.get('/', (req, res) => { /* GET /users */ });
router.get('/:id', (req, res) => { /* GET /users/:id */ });
router.post('/', (req, res) => { /* POST /users */ });

module.exports = router;
// app.js — подключаем модуль по префиксу
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);   // все маршруты внутри — с префиксом /users

express.Router — это мини-приложение со своими маршрутами, которое подключают к основному по префиксу. Маршруты пользователей, заказов, товаров живут в отдельных файлах, а app.use('/users', usersRouter) даёт им общий префикс. Это стандартная структура любого нетривиального Express-приложения — иначе один файл раздувается до тысяч строк.

Порядок маршрутов важен

// ❌ НЕВЕРНЫЙ порядок
app.get('/users/:id', handler);       // этот поймает и /users/new!
app.get('/users/new', showForm);      // сюда никогда не дойдёт

// ✅ конкретное — выше общего
app.get('/users/new', showForm);      // сначала конкретный путь
app.get('/users/:id', handler);       // потом параметрический

Express проверяет маршруты сверху вниз и берёт ПЕРВЫЙ совпавший. Поэтому /users/:id, стоящий выше /users/new, перехватит /users/new (посчитав new за :id), и до конкретного маршрута дело не дойдёт. Правило: конкретные пути — выше параметрических. Это неочевидная, но частая причина «маршрут не работает».

Обработка несуществующих путей (404)

// ... все маршруты выше ...

// в САМОМ КОНЦЕ — ловит всё, что не совпало
app.use((req, res) => {
  res.status(404).json({ error: 'Не найдено' });
});

Обработчик без пути в самом конце ловит всё несовпавшее и возвращает 404. Критично, что он стоит после всех маршрутов — иначе перехватит запросы, которые должны были дойти до реальных обработчиков. Это следствие того же правила порядка: 404-обработчик — последний рубеж.

Практика организации

  • группируйте по ресурсамroutes/users.js, routes/orders.js, каждый со своим Router;
  • конкретные маршруты выше параметрических — избегаете перехвата;
  • 404-обработчик — последним, обработчик ошибок — самым последним;
  • префиксы через app.use('/api', ...) — версионирование API (/api/v1) делается одной строкой.

Хорошая маршрутизация делает приложение читаемым: по структуре файлов видно, какие ресурсы есть и что с ними можно делать.


Смежные темы: Express с нуля — основы; middleware — общая логика для маршрутов; REST API; строка запроса. Полный список — в уроках Node.js.

Частые вопросы

Что такое маршрутизация в Express?
Сопоставление входящего запроса с обработчиком по методу HTTP и пути. Express смотрит метод (GET, POST) и URL, находит подходящий маршрут и вызывает его функцию. Это способ разложить приложение на отдельные точки входа.
Чем параметр пути отличается от строки запроса?
Параметр пути — часть URL после двоеточия, например /users/:id, и он обязателен для маршрута. Строка запроса идёт после знака вопроса, например ?sort=name, и обычно необязательна. Первое читают через req.params, второе через req.query.
Зачем нужен express.Router?
Чтобы разбить маршруты на модули, а не держать все в одном файле. Router — мини-приложение с собственными маршрутами, которое подключают к основному по префиксу. Так маршруты пользователей и заказов живут в разных файлах.
Важен ли порядок маршрутов в Express?
Да, Express проверяет маршруты сверху вниз и берёт первый совпавший. Поэтому более общие маршруты нельзя ставить выше конкретных, а обработчик несуществующих путей должен идти последним, после всех остальных.
Как обработать несуществующий маршрут?
Добавить обработчик без указания пути в самом конце — он поймает всё, что не совпало с предыдущими маршрутами, и вернёт ответ 404. Его ставят после всех маршрутов, иначе он перехватит запросы раньше времени.