smolter
Guides

Express and Node hosting

Keep your Express server, Prometheus metrics, tracing, authentication, and deployment policies.

Compatibility

ReactAvailable in this renderer's browser integration.
React SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.
PreactAvailable in this renderer's browser integration.
Preact SSRAvailable to this renderer's SSR integration; browser event handlers run after hydration.

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/server

Requests, cookies and failures

The adapter forwards methods, headers, bodies and abort signals; dispatches /path.__smolter to the data handler; and sends the Response status, headers and body. Invalid data targets return 400. Rejected handlers reach Express next(error). Express req.protocol is respected, so configure trust proxy according to your own ingress policy rather than blindly trusting forwarded headers. Multiple Set-Cookie headers are preserved. In development, the existing adapter removes Secure from outgoing cookies for local HTTP.

Entry 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" }));