Dev Proxy & Runtime Bridge¶
Running two separate servers during development (localhost:8000 for Python and localhost:5173 for Vite) traditionally introduces CORS configuration drift, split cookie origins, and broken WebSocket HMR paths behind reverse proxies. Litestar Vite eliminates two-port friction with a single-port AnyIO streaming proxy and a deterministic .litestar.json configuration bridge.
The Runtime Bridge Handshake¶
Python (ViteConfig) is the single source of truth for asset paths, ports, executors, and type-generation flags.
- Python Startup (
VitePlugin.on_app_init): Before launching or connecting to Vite,VitePluginserializes resolved paths (bundleDir,resourceDir,staticDir,assetUrl,hotFile,manifest), runtime settings (mode,proxyMode,host,port,executor,litestarPort), typegen settings (types), and static props (staticProps) into.litestar.json. - Vite Startup (
readBridgeConfig): Invite.config.ts,litestar({ input: ["src/main.ts"] })reads.litestar.jsonviareadBridgeConfig(). You never have to repeatbundleDir,assetUrl, ortypes.outputin TypeScript. - Ephemeral Port Discovery (
hotfile): Once Vite's HTTP server binds to its port,litestar-vite-pluginwrites the active dev server URL (for examplehttp://127.0.0.1:5173) to${bundleDir}/${hotFile}(public/hotby default) and removes the file automatically on process exit (SIGINT,SIGTERM,SIGHUP).
Single-Port AnyIO Streaming Proxy¶
When runtime.dev_mode=True and proxy_mode="vite" (the default), browser clients connect exclusively to the Litestar port (http://127.0.0.1:8000):
- Zero Runtime HTTPX Dependency:
ViteProxyMiddlewareuses rawanyio.connect_tcpsockets for HTTP/1.1 request/response streaming andwsprotofor WebSocket frame tunneling (vite-hmr), keeping the core package lightweight. - Dynamic Target Resolution: On each proxied request,
ViteProxyMiddlewarereads the target host and port from thehotfile (caching by mtime), so if Vite restarts on a new port, Litestar routes to the new port without restarting Python. - HMR WebSocket Tunneling (
/static/vite-hmr):litestar-vite-pluginconfigures Vite's HMR channel (server.wson Vite 8.1+ /server.hmron Vite 7) withpath: "vite-hmr", which Vite prefixes withassetUrl(/static/vite-hmr).ViteProxyMiddlewareintercepts WebSocket upgrades on that path and tunnels frames bidirectionally between the browser and Vite. - HTML Transformation (
/__litestar__/transform-index): In SPA mode,AppHandlerdelegates dev-time HTML entry transformation to Vite's internal middleware pipeline (server.transformIndexHtml) and injectswindow.__LITESTAR_CSRF__before returning the HTML shell.
Production Static Serving & Granian Native Placement¶
When dev_mode=False, ViteProxyMiddleware is not mounted. Instead, VitePlugin registers static file serving for bundle_dir and static_dir with two Router-level hooks:
| Hook Class | Responsibility | Why Attached at Router Level |
|---|---|---|
_StaticBeforeRequestHook |
Returns 404 Not Found for build metadata files (manifest.json, .vite/manifest.json, ssr-manifest.json, hot). |
Keeps StaticFilesConfig.has_asgi_overrides() equal to False so litestar-granian can serve static files directly inside Rust (StaticPlacement.NATIVE). |
_StaticAfterRequestHook |
Sets Cache-Control: public, max-age=31536000, immutable on hashed bundle paths (/static/assets/*). |
Picklable slotted callable class (__slots__) that survives copy.deepcopy(router) during Litestar route registration. |