Express: Router и модуляризация

express.Router — это мини-приложение Express, которое позволяет разбить маршруты на отдельные модули и подключать их к основному приложению через app.use.

Зачем нужно

В монолитном app.js с десятками маршрутов код быстро становится неуправляемым. express.Router позволяет группировать маршруты по ресурсам (users, products, orders) в отдельные файлы, сохраняя SRP (Single Responsibility Principle). Каждый роутер монтируется на свой префикс и может иметь собственный middleware-стек.

Где используется

  • REST API с несколькими ресурсами (/api/users, /api/products, /api/orders)
  • Версионирование API (/api/v1/..., /api/v2/...)
  • Разделение публичных и приватных маршрутов (публичные без auth, приватные — с)
  • Большие монорепозитории, где каждый модуль экспортирует свой Router

Основной контент

Структура проекта

src/
  app.js
  routes/
    users.js
    products.js
    index.js   ← точка сборки всех роутеров

Создание роутера

// routes/users.js
const express = require('express');
const router = express.Router;

// GET /api/users
router.get('/', async (req, res) => {
  const users = await UserService.getAll;
  res.json(users);
});

// GET /api/users/:id
router.get('/:id', async (req, res) => {
  const user = await UserService.getById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json(user);
});

// POST /api/users
router.post('/', async (req, res) => {
  const user = await UserService.create(req.body);
  res.status(201).json(user);
});

module.exports = router;

Точка сборки роутеров

// routes/index.js
const express = require('express');
const router = express.Router;

router.use('/users', require('./users'));
router.use('/products', require('./products'));

module.exports = router;

Подключение к приложению

// app.js
const express = require('express');
const app = express;

app.use(express.json());
app.use('/api', require('./routes'));

app.listen(3000, () => console.log('Server on port 3000'));

Middleware на уровне роутера

// Применяется только к маршрутам этого роутера
router.use(authMiddleware);

// Или для конкретного маршрута
router.get('/profile', authMiddleware, (req, res) => {
  res.json(req.user);
});

Router.param — перехват параметров

// Выполняется при наличии :userId в маршруте
router.param('userId', async (req, res, next, id) => {
  req.user = await UserService.getById(id);
  if (!req.user) return res.status(404).json({ error: 'User not found' });
  next;
});

router.get('/:userId', (req, res) => res.json(req.user));
router.delete('/:userId', (req, res) => { /* req.user уже есть */ });

Частые ошибки

  • Забыть next в middleware — запрос зависает, ответ не отправляется
  • Монтировать роутер без префиксаapp.use(router) вместо app.use('/api', router), маршруты перекрываются
  • Повторять префикс внутри роутера — если роутер смонтирован на /api/users, не нужно писать /api/users/:id внутри него, только /:id
  • Импортировать router в неправильном порядке — если middleware (express.json) подключён после роутера, req.body будет пустым

Связанные темы

Ресурсы