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-риски: безопасность, отказоустойчивость, логирование и тесты.