Маршруты: GET, POST и ответы

Маршрут — это пара "метод + путь" и функция, которая на них отвечает.
«Маршрутизация — это таблица соответствий: на такой-то запрос вызвать такую-то функцию.»

Маршруты — главная абстракция Express. Каждый маршрут отвечает на вопрос: "что делать, когда приходит запрос с методом X на путь Y?". В этом уроке мы научимся объявлять маршруты для разных методов, отдавать текст и JSON и понимать, как Express выбирает нужный обработчик из множества.

Это фундамент REST API, который мы построим позже. Сейчас важно крепко усвоить базовую механику.

Объявление маршрутов

Маршрут — это вызов метода приложения с путём и функцией-обработчиком:

const express = require('express');
const app = express();
app.use(express.json());  // чтобы читать JSON-тело

app.get('/ping', (req, res) => {
  res.send('pong');
});

app.get('/users', (req, res) => {
  res.json([{ id: 1, name: 'Аня' }, { id: 2, name: 'Боря' }]);
});

app.post('/users', (req, res) => {
  const newUser = req.body;       // тело запроса
  res.status(201).json(newUser);  // 201 = создано
});

app.listen(3000);

Метод res.json() сам сериализует объект и ставит заголовок Content-Type: application/json. Метод res.send() универсален: строку отдаст как текст, объект — как JSON.

Как Express выбирает маршрут

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

GET /users
   |
   v
app.get('/ping')   -> путь не совпал, дальше
app.get('/users')  -> совпало! вызываем обработчик -> ответ
app.post('/users') -> сюда уже не дойдём

Мини-роутер своими руками

Чтобы понять механику выбора, соберём крошечный роутер в браузере. Он хранит маршруты и находит первый подходящий — ровно как Express, только без сети:

const routes = [];
function add(method, path, handler) {
  routes.push({ method, path, handler });
}
function handle(method, path) {
  const r = routes.find(x => x.method === method && x.path === path);
  return r ? r.handler() : '404 Not Found';
}

add('GET', '/ping', () => 'pong');
add('GET', '/users', () => '[список пользователей]');

console.log(handle('GET', '/ping'));    // pong
console.log(handle('GET', '/users'));   // [список пользователей]
console.log(handle('GET', '/missing')); // 404 Not Found

По сути Express делает то же самое, только с поддержкой методов, параметров пути и middleware.

Как работает под капотом

Внутри Express каждый app.get/post/... добавляет в стек слой (layer) с регулярным выражением, в которое превращается путь. При запросе Express по очереди матчит url против этих регулярок. В Express 5 движок путей обновили на новую версию path-to-regexp, более безопасную к атакам через хитрые регулярки.

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

  • Порядок маршрутов. Более общие маршруты, объявленные раньше, могут перехватить запрос у более конкретных. Конкретное — выше, общее — ниже.
  • Забыть express.json(). Без этого middleware req.body будет пустым (в Express 5 — undefined).
  • Два ответа на один запрос. Вызов res.send() дважды приведёт к ошибке "headers already sent".

Best practices

  • Для JSON-ответов используй res.json(), а не ручную сериализацию.
  • Возвращай корректные статусы: 201 при создании, 200 при чтении.
  • Держи обработчики тонкими: приняли запрос, позвали логику, вернули ответ.

Итоги

Маршрут — это метод, путь и функция-обработчик. Express перебирает маршруты по порядку и вызывает первый подходящий. Дальше научимся делать пути гибкими: добавим параметры вроде /users/:id и query-строку.

app.all и обработка остального

Кроме методов под конкретные глаголы у приложения есть app.all('/path', ...) — он срабатывает на любой метод по этому пути, что удобно для общих проверок. А завершающий маршрут без указания пути ловит всё, что не подошло раньше, — так делают аккуратную отдачу 404: app.use((req, res) => res.status(404).json({ error: 'не найдено' })) в самом конце. Этот "catch-all" должен стоять после всех остальных маршрутов, иначе он перехватит запросы у них. Связка "конкретные маршруты сверху, 404-ловушка снизу, обработчик ошибок в самом низу" — стандартный скелет, который ты будешь воспроизводить почти в каждом приложении.

Проверьте себя
1. Что делает res.json(obj)?
AТолько логирует объект
BСериализует объект в JSON и ставит заголовок Content-Type: application/json
CСохраняет объект в базу
DВозвращает HTML-страницу
2. Почему важен порядок объявления маршрутов?
AОн не важен, Express сортирует сам
BExpress берёт первый подходящий маршрут сверху вниз
CПоследний маршрут всегда главный
DПорядок влияет только на скорость