Маршруты: 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(). Без этого middlewarereq.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-ловушка снизу, обработчик ошибок в самом низу" — стандартный скелет, который ты будешь воспроизводить почти в каждом приложении.