Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке об'єкти req та res в Express.js?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`req`** та **`res`** - це об'єкти запиту та відповіді, які Express передає в кожен обробник маршруту. `req` містить вхідні дані: параметри URL (`req.params`), query string, заголовки та body. `res` відправляє відповідь: `res.json()` для API, `res.status()` для коду статусу, `res.redirect()` для перенаправлення. ```js app.get('/user/:id', (req, res) => { res.status(200).json({ userId: req.params.id }); }); ``` **Ключове правило:** реєструй `express.json()` до маршрутів, інакше `req.body` буде `undefined`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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/:id` → `req.params.id`) - `req.query` - query string як об'єкт (`?page=2&limit=10` → `req.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-блоку, що ламає обробник з помилкою про вже надіслані заголовки.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.