JS Tower
Node.jsМодуль 4: HTTP и Express

Маршрутизация

Параметры, query strings, Router

Цель урока

В этом уроке ты научишься:

  • Работать с параметрами маршрутов
  • Использовать query параметры
  • Организовывать маршруты с Router

Параметры маршрутов

Базовые параметры

// /users/123
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;  // '123'
  res.json({ userId });
});

// /users/123/posts/456
app.get('/users/:userId/posts/:postId', (req, res) => {
  const { userId, postId } = req.params;
  res.json({ userId, postId });
});

Опциональные параметры

// /posts или /posts/123
app.get('/posts/:id?', (req, res) => {
  if (req.params.id) {
    res.json({ post: req.params.id });
  } else {
    res.json({ posts: [] });
  }
});

Регулярные выражения

// Только числа
app.get('/users/:id(\\d+)', (req, res) => {
  res.json({ id: parseInt(req.params.id) });
});

// Любой путь
app.get('/files/*', (req, res) => {
  res.json({ path: req.params[0] });
});

Query параметры

// /search?q=node&page=1&limit=10
app.get('/search', (req, res) => {
  const { q, page = 1, limit = 10 } = req.query;
  
  res.json({
    query: q,
    page: parseInt(page),
    limit: parseInt(limit)
  });
});

// /filter?tags=js&tags=node
app.get('/filter', (req, res) => {
  const tags = req.query.tags;  // ['js', 'node'] или 'js'
  
  // Гарантируем массив
  const tagsArray = Array.isArray(tags) ? tags : [tags].filter(Boolean);
  res.json({ tags: tagsArray });
});

Express Router

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

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

// Все маршруты относительно /api/users
router.get('/', (req, res) => {
  res.json([]);
});

router.get('/:id', (req, res) => {
  res.json({ id: req.params.id });
});

router.post('/', (req, res) => {
  res.status(201).json(req.body);
});

router.put('/:id', (req, res) => {
  res.json({ id: req.params.id, ...req.body });
});

router.delete('/:id', (req, res) => {
  res.status(204).send();
});

module.exports = router;

Подключение роутера

// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const postsRouter = require('./routes/posts');

const app = express();

app.use(express.json());
app.use('/api/users', usersRouter);
app.use('/api/posts', postsRouter);

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

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

router.get('/', (req, res) => res.json([]));
router.get('/:id', (req, res) => res.json({ id: req.params.id }));

// Вложенный роутер: /api/users/:userId/posts
router.use('/:userId/posts', postsRouter);

module.exports = router;

// routes/userPosts.js
const express = require('express');
const router = express.Router({ mergeParams: true });  // Важно!

router.get('/', (req, res) => {
  const { userId } = req.params;
  res.json({ userId, posts: [] });
});

module.exports = router;

Middleware для роутера

const router = express.Router();

// Middleware для всех маршрутов роутера
router.use((req, res, next) => {
  console.log('Запрос к users API');
  next();
});

// Middleware для конкретного маршрута
router.get('/:id', 
  validateId,      // Middleware 1
  checkPermission, // Middleware 2
  (req, res) => {  // Handler
    res.json({ id: req.params.id });
  }
);

function validateId(req, res, next) {
  const id = parseInt(req.params.id);
  if (isNaN(id)) {
    return res.status(400).json({ error: 'Invalid ID' });
  }
  req.userId = id;
  next();
}

function checkPermission(req, res, next) {
  // Проверка прав
  next();
}

route() метод

// Группировка методов для одного пути
router.route('/:id')
  .get((req, res) => {
    res.json({ id: req.params.id });
  })
  .put((req, res) => {
    res.json({ id: req.params.id, ...req.body });
  })
  .delete((req, res) => {
    res.status(204).send();
  });

Практика

Задание 1: API с роутером

Задача: Создай роутер для продуктов с CRUD операциями.

Решение:

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

let products = [];
let nextId = 1;

router.get('/', (req, res) => {
  const { category, minPrice, maxPrice } = req.query;
  let result = products;
  
  if (category) {
    result = result.filter(p => p.category === category);
  }
  if (minPrice) {
    result = result.filter(p => p.price >= parseFloat(minPrice));
  }
  if (maxPrice) {
    result = result.filter(p => p.price <= parseFloat(maxPrice));
  }
  
  res.json(result);
});

router.get('/:id', (req, res) => {
  const product = products.find(p => p.id === parseInt(req.params.id));
  if (!product) return res.status(404).json({ error: 'Not found' });
  res.json(product);
});

router.post('/', (req, res) => {
  const product = { id: nextId++, ...req.body };
  products.push(product);
  res.status(201).json(product);
});

module.exports = router;

Проверь себя

  1. Как получить параметр :id из URL?
  2. Что делает mergeParams: true?
  3. Как сгруппировать методы для одного пути?