Разрешение путей: алиасы через resolve.alias

Параметр server.middlewareMode в конфигурации Vite переводит dev-сервер в режим middleware. В обычном режиме Vite самостоятельно запускает HTTP-сервер, обрабатывает запросы браузера, раздаёт модули, управляет HMR и открывает страницу приложения. В режиме middleware Vite перестаёт быть самостоятельным сервером и начинает работать как промежуточный обработчик внутри другого Node.js-сервера.

Такой режим используется при интеграции Vite с:

  • Express
  • Koa
  • Fastify
  • Hapi
  • NestJS
  • собственными Node.js-серверами
  • SSR-приложениями
  • fullstack-фреймворками
  • backend + frontend монолитами

Главная идея заключается в том, что HTTP-сервер создаётся вручную, а Vite подключается к нему как middleware.


Базовая схема работы

В обычном режиме:

Браузер → Vite Server

В middleware-режиме:

Браузер → Express/Koa/Fastify → Vite Middleware

Это позволяет:

  • объединять API и frontend в одном сервере;
  • использовать собственную маршрутизацию;
  • внедрять SSR;
  • управлять авторизацией;
  • подключать прокси;
  • изменять HTML перед отправкой;
  • контролировать весь HTTP pipeline.

Включение middlewareMode

Пример конфигурации:

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    middlewareMode: true
  }
})

После включения параметра:

  • Vite больше не вызывает listen();
  • HTTP-сервер не создаётся автоматически;
  • dev-сервер используется только как middleware;
  • управление сетью полностью переходит приложению.

Интеграция с Express

Самый распространённый вариант — подключение Vite к Express.

Установка

npm install express vite

Базовый пример

import express from 'express'
import { createServer as createViteServer } from 'vite'

async function createServer() {
  const app = express()

  const vite = await createViteServer({
    server: {
      middlewareMode: true
    }
  })

  app.use(vite.middlewares)

  app.listen(3000)
}

createServer()

В этом примере:

  • Express создаёт HTTP-сервер;
  • Vite подключается через vite.middlewares;
  • все запросы проходят через Express;
  • frontend обрабатывается Vite.

Что содержит vite.middlewares

Свойство:

vite.middlewares

представляет собой готовый middleware-объект Connect.

Он включает:

  • обработку ES-модулей;
  • трансформацию файлов;
  • HMR;
  • обработку CSS;
  • обработку source map;
  • обработку /@vite/client;
  • загрузку ассетов;
  • плагины Vite;
  • HTML transformation pipeline.

Фактически это внутренний dev-сервер Vite, встроенный в приложение.


Полный SSR-сервер

middlewareMode особенно важен для SSR.

Пример:

import fs from 'fs'
import path from 'path'
import express from 'express'
import { createServer as createViteServer } from 'vite'

async function createServer() {
  const app = express()

  const vite = await createViteServer({
    server: {
      middlewareMode: true
    },
    appType: 'custom'
  })

  app.use(vite.middlewares)

  app.use('*', async (req, res) => {
    try {
      const url = req.originalUrl

      let template = fs.readFileSync(
        path.resolve('index.html'),
        'utf-8'
      )

      template = await vite.transformIndexHtml(url, template)

      const { render } = await vite.ssrLoadModule('/src/entry-server.js')

      const appHtml = await render(url)

      const html = template.replace(
        '<!--app-html-->',
        appHtml
      )

      res.status(200).set({
        'Content-Type': 'text/html'
      }).end(html)

    } catch (e) {
      vite.ssrFixStacktrace(e)
      console.error(e)
      res.status(500).end(e.message)
    }
  })

  app.listen(3000)
}

createServer()

Назначение appType: 'custom'

При SSR почти всегда используется:

appType: 'custom'

Это отключает встроенную HTML-обработку Vite.

Без этого параметра:

  • Vite попытается самостоятельно обработать HTML;
  • возможны конфликты маршрутов;
  • SSR-маршрутизация будет работать некорректно.

Как работает SSR pipeline

При запросе страницы происходит следующая цепочка:

1. Express получает запрос
2. Vite обрабатывает middleware
3. Загружается HTML-шаблон
4. Vite трансформирует HTML
5. Загружается SSR-модуль
6. React/Vue/Svelte рендерят HTML
7. HTML отправляется браузеру

Метод transformIndexHtml

Метод:

vite.transformIndexHtml(url, html)

обрабатывает HTML через pipeline Vite.

Он:

  • внедряет HMR-клиент;
  • подключает dev-скрипты;
  • применяет HTML-плагины;
  • преобразует asset URL;
  • выполняет трансформации плагинов.

Пример:

const transformed = await vite.transformIndexHtml(
  '/',
  template
)

Метод ssrLoadModule

Метод:

vite.ssrLoadModule()

загружает модуль напрямую через Vite.

Пример:

const module = await vite.ssrLoadModule(
  '/src/entry-server.js'
)

Особенности:

  • не используется bundle;
  • модули трансформируются “на лету”;
  • поддерживается TypeScript;
  • поддерживается JSX;
  • работают алиасы;
  • работают Vite plugins;
  • используется HMR.

Отличие от обычного import()

Обычный:

await import('./module.js')

использует Node.js loader.

vite.ssrLoadModule():

  • применяет Vite transforms;
  • поддерживает frontend-код;
  • обрабатывает CSS imports;
  • поддерживает virtual modules;
  • понимает alias;
  • работает с HMR.

Использование с Koa

Пример:

import Koa from 'koa'
import { createServer } from 'vite'

async function bootstrap() {
  const app = new Koa()

  const vite = await createServer({
    server: {
      middlewareMode: true
    }
  })

  app.use(async (ctx, next) => {
    await vite.middlewares(ctx.req, ctx.res, next)
  })

  app.listen(3000)
}

bootstrap()

Использование с Fastify

Пример:

import Fastify from 'fastify'
import middie from '@fastify/middie'
import { createServer } from 'vite'

async function bootstrap() {
  const app = Fastify()

  await app.register(middie)

  const vite = await createServer({
    server: {
      middlewareMode: true
    }
  })

  app.use(vite.middlewares)

  await app.listen({
    port: 3000
  })
}

bootstrap()

Использование с NestJS

Vite часто применяется как dev-layer поверх NestJS.

Пример:

const vite = await createViteServer({
  server: {
    middlewareMode: true
  }
})

app.use(vite.middlewares)

Это позволяет:

  • использовать Nest API;
  • подключать React/Vue frontend;
  • организовать SSR;
  • иметь единый dev-сервер.

Типы middlewareMode

Параметр может принимать:

middlewareMode: true

или:

middlewareMode: 'ssr'

Разница между true и 'ssr'

true

Полный middleware-режим.

Включает:

  • HTML handling;
  • asset serving;
  • HMR;
  • frontend pipeline.

'ssr'

SSR-only режим.

Используется для специализированных SSR-сценариев.

Часто применяется framework-авторами.


Когда используется 'ssr'

Режим 'ssr' полезен:

  • при собственном HTML handling;
  • при кастомном routing;
  • в framework runtime;
  • при ручном управлении render pipeline.

Работа HMR в middlewareMode

Hot Module Replacement продолжает работать полностью.

Vite автоматически:

  • создаёт websocket;
  • отслеживает файлы;
  • отправляет обновления браузеру;
  • обновляет модули без reload.

Даже если сервер полностью кастомный, HMR остаётся активным.


HMR websocket и middlewareMode

Иногда требуется отдельная настройка HMR:

server: {
  middlewareMode: true,
  hmr: {
    port: 24678
  }
}

Это полезно:

  • за reverse proxy;
  • в Docker;
  • при HTTPS;
  • в cloud development;
  • при нестандартной сетевой архитектуре.

Обработка API внутри Express

Обычно backend и frontend объединяются:

app.get('/api/users', (req, res) => {
  res.json(users)
})

app.use(vite.middlewares)

Либо наоборот:

app.use(vite.middlewares)

app.get('/api/users', handler)

Порядок middleware влияет на маршрутизацию.


Важность порядка middleware

Порядок критически важен.

Пример:

app.use(customMiddleware)
app.use(vite.middlewares)
app.use(apiMiddleware)

Каждый middleware получает запрос по цепочке.

Неправильный порядок может привести к:

  • конфликтам маршрутов;
  • ошибкам HMR;
  • неправильной обработке HTML;
  • невозможности загрузить ассеты.

Пример логирования запросов

app.use((req, res, next) => {
  console.log(req.url)
  next()
})

app.use(vite.middlewares)

Такой подход часто используется для:

  • debugging;
  • profiling;
  • audit logging;
  • analytics.

Использование proxy

Прокси продолжают работать:

server: {
  middlewareMode: true,
  proxy: {
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true
    }
  }
}

MiddlewareMode и Docker

Режим middleware особенно популярен в Docker-средах.

Причины:

  • единый контейнер;
  • единый HTTP-сервер;
  • SSR;
  • API + frontend вместе;
  • упрощённый reverse proxy.

MiddlewareMode и monorepo

В monorepo middleware-режим помогает:

  • подключать frontend как модуль;
  • объединять сервисы;
  • использовать единый backend;
  • строить fullstack architecture.

Ограничения middlewareMode

Vite не запускает сервер

Необходимо самостоятельно:

  • вызывать listen();
  • настраивать HTTPS;
  • управлять портами;
  • обрабатывать ошибки.

Больше ответственности

Разработчик самостоятельно отвечает за:

  • маршрутизацию;
  • lifecycle сервера;
  • middleware pipeline;
  • безопасность;
  • SSR flow.

Более сложная архитектура

MiddlewareMode значительно сложнее обычного:

npm create vite@latest

Он предназначен прежде всего для:

  • SSR;
  • fullstack-framework;
  • custom runtime;
  • backend integration.

Использование HTTPS

HTTPS настраивается уже на основном сервере:

https.createServer(options, app)

а не через встроенный Vite server.


Комбинация с WebSocket

Можно использовать собственные websocket-серверы:

const server = http.createServer(app)

const io = new Server(server)

при этом Vite HMR продолжит работать параллельно.


Обработка ошибок SSR

При SSR важно использовать:

vite.ssrFixStacktrace(error)

Это исправляет stack trace:

  • для TypeScript;
  • для transformed modules;
  • для source maps;
  • для virtual modules.

Без этого stack trace часто указывает на временные transformed-файлы Vite.


Dev и Production режимы

В production middlewareMode обычно заменяется готовым bundle.

Типичная схема:

Development

Express + Vite middleware

Production

Express + dist/client + dist/server

Условная инициализация

Часто используется:

const isProd = process.env.NODE_ENV === 'production'

Далее:

if (!isProd) {
  const vite = await createViteServer({
    server: {
      middlewareMode: true
    }
  })

  app.use(vite.middlewares)
}

Почему middlewareMode важен для экосистемы Vite

Именно middlewareMode сделал возможным:

  • Vite SSR;
  • Nuxt 3 dev runtime;
  • SvelteKit;
  • Astro;
  • Remix integrations;
  • custom SSR frameworks;
  • hybrid architectures.

Он превращает Vite из standalone dev-сервера в полноценный frontend runtime engine внутри серверного приложения.