Express.jsJuniorCoding

Как работает маршрутизация в Express? Как организовать маршруты с помощью express.Router()?

Express сопоставляет метод+путь с обработчиками в порядке регистрации. express.Router() создаёт модульный мини-роутер, который монтируется на app.use('/prefix', router) для разбиения маршрутов по файлам.

Маршрутизация в Express.js

Маршрутизация определяет, какой обработчик вызывается для конкретной комбинации HTTP-метода и пути. Express ищет первое совпадение сверху вниз по порядку регистрации маршрутов.

Базовая маршрутизация

import express from 'express';
const app = express();

app.use(express.json());

// HTTP-методы: get, post, put, patch, delete
app.get('/jobs', (req, res) => res.json({ jobs: [] }));
app.post('/jobs', (req, res) => res.status(201).json({ created: true }));
app.put('/jobs/:id', (req, res) => res.json({ id: req.params.id }));
app.delete('/jobs/:id', (req, res) => res.status(204).end());

// app.all — совпадает с любым методом
app.all('/ping', (req, res) => res.send('pong'));

app.listen(3000);

express.Router() — модульная маршрутизация

express.Router() создаёт мини-приложение с собственным стеком middleware и маршрутов. Это позволяет разбить маршруты по ресурсам, файлам и командам, а затем смонтировать их на основное приложение.

// routes/jobs.js
import { Router } from 'express';
import { authenticate } from '../middleware/auth.js';
import { JobController } from '../controllers/job.controller.js';

const router = Router();

// Middleware только для этого роутера
router.use((req, res, next) => {
  console.log('Jobs router hit:', req.method, req.path);
  next();
});

// Маршруты
router.get('/', JobController.list);
router.get('/:id', JobController.detail);
router.post('/', authenticate, JobController.create);
router.put('/:id', authenticate, JobController.update);
router.delete('/:id', authenticate, JobController.remove);

export default router;
// routes/users.js
import { Router } from 'express';
const router = Router();

router.get('/profile', (req, res) => res.json({ user: req.user }));
router.put('/profile', (req, res) => res.json({ updated: true }));

export default router;
// app.js
import express from 'express';
import jobsRouter from './routes/jobs.js';
import usersRouter from './routes/users.js';

const app = express();
app.use(express.json());

// Монтирование роутеров
app.use('/api/jobs', jobsRouter);   // /api/jobs/... -> jobsRouter
app.use('/api/users', usersRouter); // /api/users/... -> usersRouter

// 404 handler — регистрируется ПОСЛЕ всех роутеров
app.use((req, res) => {
  res.status(404).json({ error: `Route ${req.method} ${req.path} not found` });
});

// Error handler
app.use((err, req, res, next) => {
  res.status(err.status || 500).json({ error: err.message });
});

app.listen(3000);

Цепочки обработчиков и router.route()

// router.route() объединяет обработчики одного пути
router
  .route('/jobs/:id')
  .get(JobController.detail)
  .put(authenticate, JobController.update)
  .delete(authenticate, JobController.remove);

Вложенные роутеры

const apiRouter = Router();
apiRouter.use('/jobs', jobsRouter);
apiRouter.use('/users', usersRouter);

app.use('/api/v1', apiRouter); // /api/v1/jobs, /api/v1/users

Подводные камни

  • Порядок маршрутов критичен/jobs/new должен быть зарегистрирован до /jobs/:id, иначе строка 'new' будет интерпретирована как значение параметра.
  • 404 обработчик до error handler — 404-middleware регистрируется после всех роутов, но до обработчика ошибок (с 4 параметрами).
  • mergeParams: false по умолчанию — если дочерний роутер монтируется на путь с параметром (/users/:userId/posts), параметр userId не виден в req.params без Router({ mergeParams: true }).
  • Один экземпляр Router — одно монтирование — монтирование одного экземпляра Router на два пути приводит к неожиданному поведению; создавайте отдельные экземпляры.
  • app.use('/path', fn) vs app.get('/path', fn)app.use('/path') совпадает с любым методом и с любым подпутём (/path/anything), тогда как app.get('/path') — только с GET и точным совпадением.
  • Дублирование префикса — при монтировании app.use('/api/jobs', router) внутри роутера пишите просто / и /:id, а не /api/jobs и /api/jobs/:id.

Common mistakes

  • Дает общий ответ про Node.js и не называет конкретные API Express.js.
  • Не объясняет, где в lifecycle находится организация маршрутов через express.Router.
  • Не разделяет validation, authorization, business logic и persistence.
  • Игнорирует ошибки, лимиты входных данных, observability и тестирование.

What the interviewer is testing

  • Может объяснить организация маршрутов через express.Router на примере кода.
  • Называет ключевые API: express.Router().
  • Использует точные API Express.js, а не вымышленные hooks/decorators/methods.
  • Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.

Sources

Related topics