Express.js: Веб-фреймворк для Node.js
Express.js — это быстрый, нетребовательный, гибкий Node.js веб-фреймворк, предоставляющий богатый набор функций для создания одностраничных, многостраничных и гибридных веб-приложений, а также мобильных приложений и мощных API. Он де-факто является стандартным фреймворком для веб-разработки на Node.js.
Ключевые особенности Express.js
- Минималистичность: Express предоставляет базовую структуру веб-приложения, оставляя разработчику свободу выбора дополнительных компонентов и библиотек.
- Маршрутизация: Мощная система маршрутизации позволяет определять, как приложение отвечает на клиентские запросы к определенным URL-адресам (маршрутам).
- Промежуточное ПО (Middleware): Express использует концепцию промежуточного ПО — функций, которые имеют доступ к объекту запроса (
req), объекту ответа (res) и следующей функции промежуточного ПО в цикле "запрос-ответ" приложения. Промежуточное ПО может выполнять различные задачи, такие как логирование, аутентификация, обработка ошибок и т.д. - Система шаблонов: Поддержка различных движков шаблонов (например, Pug, EJS, Handlebars) для динамической генерации HTML.
- Статические файлы: Простое обслуживание статических файлов (HTML, CSS, JavaScript, изображения и др.).
- Надежность и производительность: Express построен на Node.js, что обеспечивает его неблокирующую и асинхронную природу, делая приложения масштабируемыми и производительными.
- Огромное сообщество и экосистема: Большое количество сторонних пакетов и промежуточного ПО доступны через npm, что позволяет легко расширять функциональность Express.
Установка
Прежде чем начать использовать Express, вам необходимо установить его в свой проект Node.js. Убедитесь, что у вас уже есть файл package.json (если нет, создайте его с помощью npm init -y).
npm install express --saveФлаг --save добавит Express в список зависимостей вашего файла package.json.
Основы использования Express.js
Создание Express-приложения
// app.js
const express = require('express');
const app = express();
const port = 3000;
// Определяем маршрут для корневого URL ('/')
app.get('/', (req, res) => {
res.send('Привет, мир от Express!');
});
// Запускаем сервер и начинаем прослушивать запросы на указанном порту
app.listen(port, () => {
console.log(`Сервер запущен на http://localhost:${port}`);
});- Импортируем модуль
express. - Создаем экземпляр Express-приложения с помощью
express(). - Определяем маршрут с помощью
app.get(). Этот маршрут обрабатывает HTTP GET-запросы к корневому URL (/).- Первый аргумент — путь маршрута.
- Второй аргумент — функция-обработчик маршрута (route handler), которая принимает объекты запроса (
req) и ответа (res). В этом примере мы отправляем текстовый ответ клиенту с помощьюres.send().
- Запускаем сервер с помощью
app.listen(), указывая порт и необязательную функцию обратного вызова, которая выполняется после успешного запуска сервера.
Чтобы запустить это приложение, выполните команду node app.js в терминале. Затем вы можете открыть браузер и перейти по адресу http://localhost:3000/, чтобы увидеть сообщение "Привет, мир от Express!".
Маршрутизация
Express предоставляет различные методы для определения маршрутов, соответствующие HTTP-методам (GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD).
// Обработка GET-запроса на '/users'
app.get('/users', (req, res) => {
res.send('Список пользователей');
});
// Обработка POST-запроса на '/users' (например, для создания нового пользователя)
app.post('/users', (req, res) => {
res.send('Создание нового пользователя');
});
// Обработка GET-запроса на '/users/:id', где ':id' — это параметр маршрута
app.get('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`Информация о пользователе с ID: ${userId}`);
});
// Обработка PUT-запроса на '/users/:id' (например, для обновления пользователя)
app.put('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`Обновление пользователя с ID: ${userId}`);
});
// Обработка DELETE-запроса на '/users/:id' (например, для удаления пользователя)
app.delete('/users/:id', (req, res) => {
const userId = req.params.id;
res.send(`Удаление пользователя с ID: ${userId}`);
});- Параметры маршрута (
:param) захватывают значения из URL и доступны черезreq.params. - Строка запроса (
?key=value&anotherKey=anotherValue) доступна черезreq.query.
// Пример использования req.query на маршруте '/search?q=keywords&page=2'
app.get('/search', (req, res) => {
const query = req.query.q;
const page = req.query.page;
res.send(`Результаты поиска по запросу: "${query}", страница: ${page}`);
});Промежуточное ПО (Middleware)
Промежуточное ПО — это функции, которые выполняются между получением запроса и отправкой ответа. Они могут выполнять различные задачи, такие как:
- Логирование: Запись информации о каждом запросе.
- Аутентификация: Проверка подлинности пользователя.
- Авторизация: Проверка прав доступа пользователя.
- Парсинг тела запроса: Обработка данных, отправленных в теле запроса (например, JSON, URL-encoded).
- Обработка ошибок: Перехват и обработка ошибок.
Express предоставляет встроенное промежуточное ПО и позволяет создавать пользовательское промежуточное ПО.
Встроенное промежуточное ПО:
express.static(root, [options]): Обслуживает статические файлы из указанной директории.
// Обслуживание статических файлов из директории 'public'
app.use(express.static('public'));express.json([options]): Парсит входящие запросы с JSON-телом. Доступ к распарсенным данным черезreq.body.
app.use(express.json());express.urlencoded({ extended: false }): Парсит входящие запросы с URL-encoded телом (обычно из HTML-форм). Доступ к распарсенным данным черезreq.body.
app.use(express.urlencoded({ extended: false }));Пользовательское промежуточное ПО:
// Промежуточное ПО уровня приложения (выполняется для всех запросов)
app.use((req, res, next) => {
console.log('Прошел запрос:', req.method, req.url);
next(); // Передает управление следующему промежуточному ПО или обработчику маршрута
});
// Промежуточное ПО уровня маршрута (выполняется только для запросов к '/admin')
const adminMiddleware = (req, res, next) => {
const isAdmin = true; // Здесь должна быть реальная логика проверки
if (isAdmin) {
next();
} else {
res.status(403).send('Доступ запрещен');
}
};
app.get('/admin', adminMiddleware, (req, res) => {
res.send('Панель администратора');
});
// Промежуточное ПО обработки ошибок (определяется с четырьмя аргументами: err, req, res, next)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Что-то пошло не так!');
});Обработка статических файлов
Как упоминалось выше, express.static() используется для обслуживания статических файлов. Предположим, у вас есть директория public в корне вашего приложения со следующей структурой:
public/
├── css/
│ └── style.css
└── images/
└── logo.pngВы можете получить доступ к этим файлам через URL-адреса /css/style.css и /images/logo.png соответственно, после использования app.use(express.static('public')).
Использование движков шаблонов
Express поддерживает множество движков шаблонов, которые позволяют динамически генерировать HTML. Для использования движка шаблонов необходимо:
- Установить соответствующий пакет (например,
npm install pug). - Настроить Express на использование этого движка.
- Создать файлы шаблонов.
- Использовать метод
res.render()для отображения шаблонов.
Пример использования Pug (ранее Jade):
// Установка: npm install pug
app.set('view engine', 'pug'); // Устанавливаем Pug в качестве движка шаблонов
app.set('views', './views'); // Указываем директорию с файлами шаблонов
app.get('/template', (req, res) => {
res.render('index', { title: 'Привет', message: 'Добро пожаловать на мою страницу!' });
});Здесь res.render('index', { ... }) отобразит файл index.pug, находящийся в директории views, и передаст ему объект с данными (title и message).
Стороннее промежуточное ПО
Существует огромное количество стороннего промежуточного ПО для Express, доступного через npm, которое может выполнять различные задачи, такие как:
body-parser(устаревший, теперь встроен в Express): Парсинг тела запроса (JSON, URL-encoded, raw, text).cookie-parser: Парсинг и установка куки.express-session: Управление сессиями.passport: Аутентификация.cors: Обработка CORS (Cross-Origin Resource Sharing).morgan: Логирование HTTP-запросов.
Для использования стороннего промежуточного ПО его необходимо установить с помощью npm и затем подключить к вашему приложению с помощью app.use().
Production: безопасность и надежность
Express дает базовый каркас, но “из коробки” не делает приложение безопасным и готовым к продакшену. Обычно добавляют набор практик, которые закрывают самые частые риски.
Минимальный security baseline
const express = require('express')
const app = express()
app.disable('x-powered-by')
app.use(express.json({ limit: '1mb' }))Рекомендации:
- отключайте заголовок
X-Powered-By; - ограничивайте размер тела запроса, особенно для публичных API;
- аккуратно настраивайте CORS: разрешайте только нужные домены и методы.
Заголовки безопасности и защита от злоупотреблений
Часто подключают пакеты:
helmet— безопасные HTTP-заголовки;express-rate-limit— ограничение частоты запросов.
npm i helmet express-rate-limitconst helmet = require('helmet')
const rateLimit = require('express-rate-limit')
app.use(helmet())
app.use(
rateLimit({
windowMs: 60_000,
max: 100
})
)Валидация входных данных
Всегда валидируйте:
- параметры маршрута (
req.params); - query (
req.query); - тело запроса (
req.body).
Нужно защищаться и от “плохих типов”, и от семантики (пустые значения, слишком длинные строки, невалидные enum-значения).
Обработка ошибок и async
Сделайте единый обработчик ошибок и старайтесь возвращать единый формат ответа.
app.use((err, req, res, next) => {
const status = err.statusCode ?? 500
res.status(status).json({
message: err.message ?? 'Internal Server Error'
})
})Для асинхронных обработчиков удобно использовать обертку, чтобы не писать try/catch в каждом маршруте:
const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next)
app.get(
'/users',
asyncHandler(async (req, res) => {
const users = await loadUsers()
res.json(users)
})
)Graceful shutdown
В продакшене процесс должен корректно завершаться (например, при деплое). Минимально — обработка SIGTERM:
const server = app.listen(3000)
process.on('SIGTERM', () => {
server.close(() => process.exit(0))
})Структура Express-приложения
Нет строгих правил относительно структуры Express-приложения, но обычно рекомендуется разделять код на логические блоки:
my-express-app/
├── node_modules/
├── public/ // Статические файлы (CSS, JS, images)
├── routes/ // Файлы с определениями маршрутов
│ ├── users.js
│ └── products.js
├── middleware/ // Пользовательское промежуточное ПО
├── views/ // Файлы шаблонов
├── app.js // Основной файл приложения
├── package.json
└── package-lock.jsonЗаключение
Express.js предоставляет мощный и гибкий набор инструментов для создания веб-приложений и API на Node.js. Его минималистичная природа и концепция промежуточного ПО позволяют разработчикам легко расширять функциональность и строить приложения любой сложности. Благодаря огромному сообществу и богатой экосистеме, Express остается одним из самых популярных и востребованных фреймворков для Node.js.