Skip to main content

Що таке об'єкти req та res в Express.js?

req та res - це два об'єкти, які Express передає в кожен обробник маршруту. req містить всі дані вхідного HTTP-запиту. res дає інструменти для відправки відповіді.

Теорія

TL;DR

  • req = вхідні дані: параметри URL, query string, заголовки, body
  • res = інструменти відповіді: .json(), .status(), .redirect(), .send()
  • req - тільки для читання за конвенцією; res - тільки для запису
  • Body-парсинг: express.json() має стояти до маршрутів, інакше req.body буде undefined
  • Відповідь надсилається один раз на запит, завжди return при ранніх виходах

Швидкий приклад

js
const express = require('express'); const app = express(); app.use(express.json()); // заповнює req.body app.get('/user/:id', (req, res) => { const id = req.params.id; // "123" - сегмент URL const name = req.query.name; // "Alice" - з ?name=Alice const token = req.get('Authorization'); // значення заголовка res.status(200).json({ id, name }); // відправляємо JSON }); app.listen(3000); // GET /user/123?name=Alice → { "id": "123", "name": "Alice" }

req витягує три різні шматки даних з одного URL. res відправляє одну структуровану відповідь. Ось і вся модель.

Що містить req

req базується на Node's http.IncomingMessage, але Express додає поверх нього розпарсені властивості. Найчастіше використовуються такі:

  • req.params - сегменти URL, що відповідають шаблону маршруту (/user/:idreq.params.id)
  • req.query - query string як об'єкт (?page=2&limit=10req.query.page)
  • req.body - розпарсений payload запиту, доступний після express.json() або express.urlencoded()
  • req.headers / req.get('Name') - вхідні заголовки
  • req.method, req.path, req.protocol, req.ip - метадані запиту

Middleware може також додавати власні властивості. Passport.js записує req.user, Multer записує req.file, і ти можеш призначати свої у будь-якому middleware.

Що робить res

res базується на Node's http.ServerResponse, розширений хелперами Express. Основні методи:

  • res.json(data) - відправляє JSON з Content-Type: application/json
  • res.send(text) - відправляє текст або HTML, автоматично визначає content type
  • res.status(code) - встановлює HTTP статус, повертає res для ланцюгування
  • res.redirect('/path') - відправляє 302 redirect; res.redirect(301, '/path') для постійного
  • res.sendFile('/abs/path') - стримить файл клієнту
  • res.set('Header', 'value') - встановлює заголовок відповіді

Більшість методів res повертають сам res, тому ланцюгування працює: res.status(201).json({ ok: true }).

res.locals - звичайний об'єкт, що живе один цикл запиту. Middleware записує дані туди, обробники маршрутів їх читають:

js
app.use((req, res, next) => { res.locals.requestId = crypto.randomUUID(); next(); }); app.get('/profile', (req, res) => { res.json({ requestId: res.locals.requestId }); });

Коли що використовувати

  • Дані POST або PATCH → req.body (після body-parser middleware)
  • Ідентифікатори ресурсу в URL → req.params
  • Фільтри, сортування, пагінація → req.query
  • Auth-токен → req.get('Authorization')
  • JSON-відповідь API → res.json()
  • Відповіді з помилками → res.status(400).json({ error: '...' })
  • Перенаправлення → res.redirect('/path')

Типові помилки

Звернення до req.body без body-парсера

js
// Неправильно: req.body буде undefined app.post('/login', (req, res) => { console.log(req.body); // undefined }); // Виправлення: реєструємо до маршрутів app.use(express.json());

Відсутній return при ранніх відповідях

js
// Неправильно: надсилає дві відповіді, кидає "Cannot set headers after they are sent" app.get('/user/:id', (req, res) => { if (!req.params.id) res.status(400).send('No ID'); res.json({ ok: true }); // завжди виконується }); // Виправлення app.get('/user/:id', (req, res) => { if (!req.params.id) return res.status(400).send('No ID'); res.json({ ok: true }); });

Це найпоширеніша помилка в Express-коді на code review. Перша відповідь іде нормально, але обробник продовжує виконуватись і падає на другому виклику res.

Виклик res.json() після res.write()

Коли починаєш стримінг через res.write(), заголовки вже надіслані. Виклик res.json() після цього кидає помилку. Завершуй стримінг тільки через res.end().

Логування повного req.body в продакшені

js
// Паролі та персональні дані потраплять у логи console.log(req.body); // Логуй тільки те, що потрібно console.log({ email: req.body.email });

Де зустрічається в реальному коді

  • Stripe webhooks: читаємо event з req.body, відповідаємо res.status(200).end() одразу, щоб не спрацював таймаут
  • Passport.js: читаємо req.user (встановлений auth middleware), потім res.redirect('/dashboard')
  • Завантаження файлів через Multer: звертаємось до req.file.path після upload middleware
  • Сесійні куки: читаємо req.cookies.sessionId, встановлюємо нову через res.cookie('token', value, { httpOnly: true })

Питання на співбесіді

Q: Яка різниця між req.params, req.query та req.body?


A: req.params приходить з іменованих сегментів URL, наприклад /user/:id. req.query - з query string ?key=val. req.body - з payload POST або PUT запиту і потребує middleware-парсера.

Q: Що станеться, якщо викликати res.send() двічі?


A: Express кидає ERR_HTTP_HEADERS_SENT. Перший виклик закриває потік відповіді. Завжди став return після будь-якого виклику res, що завершує обробник.

Q: Як express.json() заповнює req.body всередині?


A: Він слухає події data та end на req, збирає chunks у буфер, потім викликає JSON.parse() на зібраному рядку. Ліміт за замовчуванням - 100kb, що захищає від вичерпання пам'яті.

Q: Як отримати реальний IP клієнта, коли Express стоїть за Nginx?


A: Встанови app.set('trust proxy', 1), потім читай req.ip. Без цього Express читає адресу самого проксі-сервера замість значення заголовка X-Forwarded-For.

Приклади

Пошук товару: params і query разом

js
const express = require('express'); const app = express(); app.get('/products/:id', (req, res) => { const productId = req.params.id; const currency = req.query.currency || 'USD'; // В реальному коді тут запит до БД res.json({ productId, currency }); // GET /products/42?currency=EUR → { "productId": "42", "currency": "EUR" } }); app.listen(3000);

Чистий патерн для GET: req.params для ідентифікатора ресурсу, req.query для опцій, res.json() для відповіді.

Обробник логіну: валідація body і ранні виходи

js
app.use(express.json()); app.post('/login', (req, res) => { const { email, password } = req.body; if (!email || !password) { return res.status(400).json({ error: 'Missing credentials' }); } if (email === 'user@example.com' && password === 'secret') { return res.json({ token: 'jwt-token', user: { email } }); } res.status(401).json({ error: 'Invalid credentials' }); });

Кожен ранній вихід використовує return. Без нього Node намагається надіслати другу відповідь після виходу з if-блоку, що ламає обробник з помилкою про вже надіслані заголовки.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.

Дочитали статтю?