Как тестировать фронт и бэк с HMR без сборки
При разработке веб-приложений часто возникает задача: запустить одновременно фронтенд и бэкенд с поддержкой горячей перезагрузки модулей (HMR - Hot Module Replacement). Стандартный подход - собрать фронт через npm run build и разместить статику в папке бэка, но это убивает HMR. Разберём, как настроить dev-среду, чтобы оба сервера работали параллельно и изменения применялись мгновенно.
Проблема: билд фронта убивает HMR
Когда вы запускаете npm run build, сборщик (например, Webpack или Vite) создаёт оптимизированные статические файлы. Они кладутся в папку бэка, после чего запускается node server.js. В этом режиме HMR не работает - любое изменение фронта требует повторного билда. Для разработки это неудобно.
Решение: параллельный запуск dev-режимов
Оптимальный способ - запустить фронт в dev-режиме (с HMR) и бэк в dev-режиме (например, с nodemon или ts-node-dev), а затем настроить прокси на бэкенде, чтобы он перенаправлял запросы к API и отдавал статику из dev-сервера фронта.
Шаг 1. Запустите фронт в dev-режиме
Для проектов на React/Vue/Angular обычно используется команда npm run dev. Она запускает dev-сервер (например, на порту 3000) с HMR. Убедитесь, что все изменения сохраняются и страница перезагружается автоматически.
Шаг 2. Запустите бэк в dev-режиме
Для Node.js-бэкенда используйте nodemon или ts-node-dev с флагом --watch. Команда может выглядеть так: nodemon server.js или ts-node-dev --respawn src/server.ts. Бэк должен слушать свой порт (например, 5000).
Шаг 3. Настройте прокси на бэкенде
В бэкенде (Express, Koa, Fastify) добавьте middleware для проксирования запросов к статике на dev-сервер фронта. Пример для Express:
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Прокси для статики фронта
app.use('/', createProxyMiddleware({
target: 'http://localhost:3000', // порт dev-сервера фронта
changeOrigin: true
}));
// API-маршруты
app.use('/api', apiRouter);
app.listen(5000);Теперь при обращении к http://localhost:5000 бэк отдаёт статику с фронтового dev-сервера, а API-запросы обрабатывает сам.
Шаг 4. Используйте инструменты для параллельного запуска
Чтобы не запускать два терминала вручную, установите concurrently или npm-run-all. Пример скрипта в package.json:
"scripts": {
"dev": "concurrently \"npm run dev:front\" \"npm run dev:back\"",
"dev:front": "cd frontend && npm run dev",
"dev:back": "cd backend && nodemon server.js"
}Теперь одной командой npm run dev запускаются оба сервера с HMR.
Альтернативные подходы
- Использование одного порта через reverse proxy - nginx или Caddy могут выступать единой точкой входа, перенаправляя запросы на разные порты.
- Монорепозиторий с общей конфигурацией - инструменты вроде Nx или Turborepo позволяют управлять зависимостями и запускать dev-серверы для нескольких приложений.
- Docker Compose - если вы используете контейнеризацию, можно поднять два контейнера и связать их через внутреннюю сеть.
Почему это работает
Dev-сервер фронта поддерживает HMR и отдаёт обновлённые модули через WebSocket. Бэкенд не трогает статику - он только проксирует запросы. Таким образом, изменения в коде фронта мгновенно отображаются в браузере, а бэк перезагружается при изменении своих файлов. Никаких билдов, только разработка.