Server-Side Rendering & IPC Transports#
litestar-vite executes server-side rendering (SSR) and component fragments using Vite 7+’s RunnableDevEnvironment in development and a managed stdio child process in production, through a dedicated IPC layer. Development SSR uses a managed httpx2 connection pool inside TCPStreamIPCTransport to call the existing Vite server. Asset and framework HTTP proxying use their own managed clients. Production SSR communicates over stdio.
Development vs. Production Model#
litestar-vite separates development and production SSR execution into two purpose-built paths:
Mode |
Transport |
Execution Model |
|---|---|---|
Development ( |
|
Evaluates |
Production ( |
|
Spawns the compiled SSR bundle (for example |
TCPStreamIPCTransport establishes its client pool lazily and releases connections
on close. It supports HTTP and HTTPS endpoints, with HTTP framing and response
decoding handled by httpx2. Development rendering runs inside the existing Vite
process; it does not start another SSR process.
Why Not a Fixed Port Daemon?#
Running a standalone Node.js HTTP server on a fixed port (such as 127.0.0.1:13714) has operational drawbacks that litestar-vite avoids:
Zero Port Collisions: In development, SSR shares the existing Vite dev server port over
/__litestar_ssr__. In production, communication happens over anonymous OSstdin/stdoutpipes with no listening socket or port allocation.Pipe-Bound Process Lifecycle: In production, the SSR worker’s lifetime is bound directly to its
stdinpipe. When the Litestar worker exits, the OS closes the pipe, the Node/Bun worker receivesEOFonstdin, and the child process terminates immediately without leaving orphaned background processes.HMR Cache Invalidation in Dev: Because development SSR runs inside Vite’s
RunnableDevEnvironment, edits to Vue, React, or Svelte components invalidate the module and its importer chain in memory without rebuilding an SSR bundle.
Production Stdio Worker Protocol#
In production, StdioIPCTransport spawns the SSR bundle with anyio.open_process() and multiplexes concurrent render requests over stdin/stdout using newline-delimited JSON messages keyed by integer correlation IDs:
sequenceDiagram
autonumber
participant App as Litestar Application
participant Transport as StdioIPCTransport
participant Pipe as OS Pipe (stdin/stdout)
participant Worker as Node/Bun SSR Worker
App->>Transport: start()
Transport->>Pipe: anyio.open_process(command)
Transport->>Transport: Start background stdout & stderr readers
App->>Transport: send_request({"method": "render", "params": page})
Transport->>Pipe: stdin.send({"id": 1, "method": "render", "params": page}\n)
Pipe->>Worker: readline "line" event
Worker->>Worker: await render(page)
Worker->>Pipe: stdout.write({"id": 1, "result": {"head": [...], "body": "..."}}\n)
Pipe->>Transport: stdout reader correlates response id=1
Transport-->>App: Return render result dict
App->>Transport: close()
Transport->>Pipe: stdin.aclose() (EOF)
Pipe->>Worker: readline "close" event
Worker->>Worker: process.exit(0)
StdioIPCTransport resolves platform executable shims (.cmd / .exe on Windows) via shutil.which and continuously drains the child stderr stream in a background task so pipe buffers never block.
Dual-Mode SSR Entrypoint#
Generated resources/ssr.ts / resources/ssr.tsx entrypoints support both modes in a single file:
They export
default async function render(page)so Vite’sRunnableDevEnvironment.runnercan import and invokerender(page)directly in development.When executed outside Vite dev mode (
if (!import.meta.env?.DEV)), they start anode:readlineloop overprocess.stdinand write JSON responses toprocess.stdout.
Python Configuration#
Enable SSR in InertiaConfig:
from pathlib import Path
from litestar import Litestar
from litestar_vite import InertiaConfig, InertiaSSRConfig, PathConfig, ViteConfig, VitePlugin
vite_config = ViteConfig(
paths=PathConfig(
root=Path(__file__).parent,
resource_dir="resources",
bundle_dir="public",
),
inertia=InertiaConfig(
ssr=InertiaSSRConfig(
command=["node", "resources/bootstrap/ssr/ssr.js"],
),
),
)
vite_plugin = VitePlugin(config=vite_config)
app = Litestar(plugins=[vite_plugin])
Or instantiate an IPC transport directly:
from litestar_vite.ipc import StdioIPCTransport, TCPStreamIPCTransport
stdio_transport = StdioIPCTransport(
command=["node", "resources/bootstrap/ssr/ssr.js"],
max_restarts=3,
)
dev_transport = TCPStreamIPCTransport(
host="127.0.0.1",
port=5173,
path="/__litestar_ssr__",
)