Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке Express.js і чому його використовують?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Express.js** - це мінімальний веб-фреймворк для Node.js, який додає маршрутизацію, middleware та HTTP-утиліти поверх вбудованого модуля `http`. ```js app.use(express.json()); app.get('/users', (req, res) => res.json([{ id: 1, name: 'Alice' }])); app.listen(3000); ``` **Головне:** Express перетворює 15+ рядків ручного парсингу URL на 3 декларативні рядки маршрутів. Бери для будь-якого Node.js веб-додатку, якщо не потрібні 10k+ req/s (Fastify) або нуль залежностей.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Express.js** - це мінімальний веб-фреймворк для Node.js, який додає маршрутизацію, middleware та HTTP-утиліти поверх вбудованого модуля `http`. ## Теорія ### TL;DR - Express для Node.js, як підготовча станція для кухаря: Node дає тобі піч і сировину, Express додає ножі, дошки і рецепти - Головна різниця: 15+ рядків ручного парсингу запитів у Node.js стають 3 декларативними рядками у Express - Express автоматично обробляє URL-маршрутизацію, парсинг JSON і заголовки відповідей - Правило вибору: бери Express для будь-якого Node.js веб-додатку, якщо не потрібні нуль залежностей або 10k+ req/s (тоді Fastify) ### Швидкий приклад ```js // Чистий Node.js: ручна маршрутизація, ручні заголовки, все вручну const http = require('http'); const server = http.createServer((req, res) => { if (req.url === '/users' && req.method === 'GET') { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify([{ id: 1, name: 'Alice' }])); } else { res.writeHead(404); res.end('Not Found'); } }); server.listen(3000); // Express: той самий результат, 3 рядки const express = require('express'); const app = express(); app.get('/users', (req, res) => res.json([{ id: 1, name: 'Alice' }])); app.listen(3000); ``` Обидва дають однакову відповідь. Express прибирає весь рядковий пошук по `req.url` і ручне виставлення заголовків. ### Що Express насправді робить Модуль `http` у Node.js дає тобі сирий об'єкт запиту: рядок URL, рядок методу і тіло як буфер. Все потрібно парсити самостійно. Express обгортає цей об'єкт роутером (radix-дерево для швидкого пошуку шляхів), підключає парсери через middleware і дає тобі `res.json()`, `res.status()` та інші хелпери. Ти пишеш обробники маршрутів замість ланцюжків if/else. Стек middleware (проміжних функцій) - це центральна ідея. Кожен виклик `app.use()` додає функцію в чергу. Коли приходить запит, Express запускає кожну функцію по порядку, передаючи через них `req` і `res`. Якщо middleware викликає `next()`, запускається наступна функція. Якщо викликає `res.send()`, ланцюжок там і зупиняється. ### Коли використовувати Express - REST API або веб-додаток: Express - вибір за замовчуванням - Прототипування: найшвидший шлях від ідеї до запущеного сервера - Full-stack додаток з авторизацією і базою даних: Express плюс стек middleware (cors, helmet, body-parser) - Serverless або edge-функції з багатьма маршрутами: Express підходить; менше 10 маршрутів, hono.js буде легшим - Мікросервіс з кастомним протоколом: пропускай Express, використовуй `http` напряму - 10k+ запитів на секунду в продакшені: Fastify приблизно вдвічі швидший; Express впевнено тягне до ~5k req/s ### Альтернативи на вибір | Фреймворк | Стиль | Найкраще підходить | |---|---|---| | Express | Мінімальний, гнучкий | REST API, full-stack додатки | | Fastify | Швидкий, на основі схем | Навантажені API | | Koa | Async-first, менше ядро | Нові проекти, кастомний middleware | | NestJS | Структурований, TypeScript | Корпоративні додатки | | hono.js | Компактний, для edge | Serverless, Cloudflare Workers | Koa варто знати для співбесіди. Він використовує async/await нативно там, де Express використовує callbacks, і його ядро значно менше. Для нових проектів Koa - цілком валідний вибір. Коли потрібна велика екосистема плагінів, Express виграє. ### Типові помилки **Відсутній `express.json()` перед маршрутами.** Це ламає приблизно 40% перших Express-додатків. Без middleware `req.body` залишається `undefined`, і будь-який доступ до JSON падає з помилкою. ```js // Неправильно: req.body тут undefined app.post('/user', (req, res) => res.send(req.body.name)); // TypeError // Правильно: парсити JSON глобально перед будь-яким маршрутом app.use(express.json()); app.post('/user', (req, res) => res.send(req.body.name)); // "Alice" ``` **Неправильний порядок middleware.** Middleware запускається зверху вниз. Якщо зареєструвати маршрут до `express.json()`, той маршрут отримає непарсинговане тіло. ```js // Неправильно: маршрут бачить req.body як undefined app.post('/api/user', (req, res) => res.send(req.body.name)); app.use(express.json()); // запізно // Правильно app.use(express.json()); // обов'язково першим app.post('/api/user', (req, res) => res.send(req.body.name)); // працює ``` **Подвійний виклик `res.send()`.** Node кидає "Cannot set headers after they are sent". Виправляється раннім `return`. ```js // Неправильно: два send в одному обробнику if (err) res.status(500).send('Error'); res.json({ ok: true }); // краш // Правильно if (err) return res.status(500).send('Error'); res.json({ ok: true }); ``` **Відсутній `next()` у middleware.** Запит зависає. Клієнт не отримує відповідь, в продакшен-моніторах спрацьовують таймаути. ```js // Неправильно: запит зависає назавжди app.use((req, res, next) => { console.log(req.method); // забули next() }); // Правильно app.use((req, res, next) => { console.log(req.method); next(); }); ``` ### Де зустрічається в реальних проектах - **Strapi**: headless CMS з 1M+ завантаженнями, побудований на Express для REST-ендпоінтів і адмін-панелі - **Ghost**: блогова платформа, Express обробляє динамічні маршрути і теми - **PayloadCMS**: Express плюс TypeScript для e-commerce бекендів, 10k+ розробників - **Next.js API routes**: внутрішньо використовує Express-подібний роутер для обробників `/pages/api` - Більшість внутрішніх Node.js інструментів стартують з Express, а до Fastify переходять лише тоді, коли throughput стає реальною проблемою Я помічав, що команди використовують Express роками без досягнення його performance-межі, бо справжнє вузьке місце - це запит до бази даних. Якщо запит займає 50ms, overhead Express у 0.1ms просто не має значення. ### Питання на співбесіді **Q:** Що таке middleware у Express і як це працює? **A:** Middleware - це функція з сигнатурою `(req, res, next)`. Express викликає їх по порядку для кожного запиту. Використовується для парсингу тіла, перевірки авторизації, логування або додавання даних до `req`. Якщо middleware не викликає `next()`, ланцюжок зупиняється і клієнт не отримує відповідь. **Q:** Як Express обробляє async-помилки? **A:** Автоматично - ніяк. Якщо в async-обробнику виникає виняток, Express його не зловить. Потрібно або загортати кожен обробник у try/catch і викликати `next(err)`, або використовувати пакет `express-async-errors`, який патчить роутер для перехоплення відхилень Promise. **Q:** В чому різниця між `app.get()` і `router.get()`? **A:** `app` - це глобальний екземпляр Express. `router` - це міні-Express-додаток, який можна монтувати на будь-який шлях. Використовуй `router` для групування маршрутів за префіксом: `app.use('/api', router)` монтує всі маршрути роутера під `/api`. Це тримає великі кодові бази модульними без одного гігантського файлу. **Q:** Як маршрутизація Express масштабується до сотень маршрутів? **A:** Express використовує radix-дерево (layered path trie) для пошуку маршрутів. Пошук займає O(k), де k - довжина шляху, а не O(n) маршрутів. Тому 1000 маршрутів не уповільнять пошук порівняно з 10. **Q:** Express проти Koa - коли обрати Koa? **A:** Koa використовує async/await нативно і має менше ядро без вбудованого middleware. Гарний вибір для нових проектів, де потрібен повний контроль над стеком. Express має більшу екосистему плагінів і більше прикладів у спільноті. Команди на наявних кодових базах зазвичай залишаються з Express, бо вартість переходу не виправдана. ## Приклади ### Базовий REST API з парсингом JSON ```js const express = require('express'); const app = express(); app.use(express.json()); // парсинг JSON у тілі запиту app.get('/users', (req, res) => { res.json([{ id: 1, name: 'Alice' }]); }); app.post('/users', (req, res) => { const { name } = req.body; // працює завдяки express.json() res.status(201).json({ id: 2, name }); }); app.listen(3000, () => console.log('Запущено на порту 3000')); ``` `app.use(express.json())` виконується перед кожним маршрутом. POST до `/users` з JSON-тілом автоматично отримує заповнений `req.body`. Без цього рядка `req.body` залишається `undefined` і деструктуризація падає. ### Обробник webhook з правильним порядком middleware Ось як виглядає перевірка Stripe webhook у реальному Express-додатку: ```js const express = require('express'); const app = express(); // Сирий буфер потрібен для перевірки підпису - обов'язково до express.json() app.use('/webhook/stripe', express.raw({ type: 'application/json' })); app.use(express.json()); // JSON для всього іншого app.post('/webhook/stripe', (req, res) => { const sig = req.headers['stripe-signature']; if (!verifySignature(sig, req.body)) { return res.status(400).send('Invalid signature'); } console.log('Event:', req.body.type); // 'payment_intent.succeeded' res.json({ received: true }); }); app.listen(3000); ``` Маршрут `/webhook/stripe` потребує сирого буфера для перевірки підпису. Все інше отримує парсингований JSON. Порядок middleware контролює, який парсер спрацює першим, тому специфічний маршрут реєструється до глобального. Це реальний патерн з PayloadCMS та подібних проектів. Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.