Express and Node hosting
Keep your Express server, Prometheus metrics, tracing, authentication, and deployment policies.
Compatibility
Middleware ownership
Smolter does not start a listening server or depend on Express. createRequestHandler is a Node-compatible middleware function. Mount metrics, tracing, authentication, request limits, and static assets in your existing Express app before it. Mount error-handling middleware afterward. Do not put express.json() or express.urlencoded() before Smolter routes: the adapter reads the original request body for route actions.
import express from "express";
import { createRequestHandler } from "@kasperrt/smolter/utils/http";
import * as build from "./dist/server/index.js";
import manifest from "./dist/client/.vite/manifest.json" with { type: "json" };
const app = express();
const client = manifest["index.html"];
// Mount your Prometheus and tracing middleware here, before Smolter.
app.use("/assets", express.static("dist/client/assets", {
immutable: true,
maxAge: "1y",
}));
app.use(createRequestHandler({
build,
entry: `/assets/${client.file.replace(/^assets\//, "")}`,
}));
app.listen(3000);Build contract
Pass the server entry module as build. It exports default(request, entry) for documents and handleDataRequest(request) for loaders/actions. entry is the browser entry URL from the Vite manifest, including your deployment base. For the application integration, enable build.manifest in your browser Vite config, then build the server entry separately.
vite build --outDir dist/client --manifest
vite build --ssr src/entry.server.tsx --outDir dist/serverEntry files
Your metrics and tracing middleware wraps the SSR/action handler. Node helpers stream Response bodies with backpressure and flush compression middleware when supported. Keep proxies configured to permit streaming. Server code stays in server-only subpaths.
Multiple applications on one origin
Route-local transport preserves application prefixes: /admin/users.__smolter dispatches /admin/users. Mount the correct application and authentication middleware for both document and data paths. Reserve the suffix before static fallbacks. Existing Express endpoints at /admin/users remain separate. Explicit dataEndpoint retains the legacy query transport for migration.
app.use("/admin", adminAuthentication, createRequestHandler({ build, entry: "/admin/assets/client.js" }));