Server Entry
Import
import entryServer from '@lomray/vite-ssr-boost/adapters/express/entry';Signature
entryServer(App, routes, options?)Options:
interface IEntryServerOptions<TAppProps> {
ssr?: ISsrPolicy;
requestGuard?: IRequestGuardOptions | false;
notFound?: TNotFoundOptions;
admission?: IAdmissionOptions;
abortDelay?: number;
init?: (params: {
config: ServerConfig;
}) => IEntrypointOptions<TAppProps> | Promise<IEntrypointOptions<TAppProps>>;
loggerProd?: Logger;
loggerDev?: Logger;
middlewares?: {
compression?: CompressionOptions | false;
expressStatic?: (ServeStaticOptions & { basename?: string }) | false;
};
routerOptions?: Parameters<typeof createStaticHandler>[1];
}What it returns
The entry returns a render definition consumed by the runtime server. It includes:
renderinitroutesabortDelay- optional loggers
- optional middleware config
Request guard, 404 modes and admission
The request guard is enabled by default, before onRequest and HTML loading. Configure requestGuard, notFound and admission at the entry level (also available on Fetch handlers). See Request guard and admission for defaults, the behavior-change notice, anonymous cached 404s and a hardening preset.
ssr
Select SSR or the SPA shell per incoming URL, before route loaders run:
interface ISsrPolicy {
mode?: 'all' | 'include' | 'exclude';
routes?: (string | RegExp)[];
bots?: 'ssr' | 'policy';
decide?: (params: { request: Request; url: URL; isBot: boolean }) => 'ssr' | 'spa' | undefined;
}
entryServer(App, routes, {
ssr: { mode: 'include', routes: ['/', '/articles/:slug'] },
});all is the default. include uses SSR only for matching URL pathnames; exclude serves matches as SPA. Strings use path-to-regexp 8 syntax, including named wildcards and brace optional groups; RegExp patterns use their own flags. Include the router basename in patterns. decide overrides the configured mode per request, with undefined falling back to the route policy. bots: 'ssr' defaults to forcing detected crawlers to SSR ahead of all other decisions; use 'policy' to opt out.
At entry creation, SSR_BOOST_SSR_ROUTES overrides mode, routes and decide, preserving bots. Comma-separated positive patterns form an include list; ! patterns exclude URLs and win over includes. With only exclusions, other URLs stay SSR. An empty value selects SSR everywhere. Restart after changes; no rebuild is needed.
SPA responses retain onRequest and route asset preparation, use status 200 and the data-force-spa mount marker, and omit router/custom state and SSR render hooks. Active policies default documents to Cache-Control: no-store unless onRequest supplies a cache policy. The same ssr option is available on Fetch createHandler. See Incremental SSR for the full reference, lifecycle trade-offs and rollback recipe.
Request lifecycle hooks
init resolves to a request lifecycle configuration:
interface IEntrypointOptions<TAppProps> {
hydration?: 'footer' | 'early';
nonce?: string;
bootstrapScriptContent?: string;
onServerCreated?;
onServerStarted?;
onRequest?;
onRouterReady?;
onShellReady?;
onShellError?;
onResponse?;
onError?;
getState?;
}See Server Lifecycle for the flow and intent of each hook.
Hook context
onRouterReady, onShellReady, onShellError, onError, onResponse and getState receive { context }, with these fields:
request: the FetchRequestbuilt from the Express request; use it for headers, URL and method so hooks stay portable to other adapters.response: mutable Fetchheadersand optionalstatus; update these before the shell is sent.req/res: the live Express request and response, deprecated in 8.x with removal planned for 9.0.appProps: request-scoped props returned byonRequest.html: the templateheaderandfooter.routerContext/serverContext: router and SSR metadata, once available.isStream,hasEarlyHintsanddidError: rendering mode, early-hints preference and error metadata.
request is the same object used by the Fetch core throughout a render; its signal tracks request cancellation.
onRouterReady: ({ context: { request } }) => ({
isStream: !request.headers.get('user-agent')?.includes('Googlebot'),
}),onRequest
The most important request hook.
It receives (req, res) before the render context is created.
It can return:
{
appProps?: TAppProps;
hasEarlyHints?: boolean;
shouldSkip?: boolean;
shouldCancel?: boolean;
}That lets you shape app props, skip a request, or stop the rendering path entirely.
onResponse
onResponse?: (params: {
context: IRequestContext<TAppProps>;
html: string;
isEnd: boolean;
}) => string | undefined | void;Regular HTML chunks arrive with isEnd: false. Returning undefined (or nothing) keeps the original chunk; a string replaces it. Returning '' withholds the chunk, allowing an incremental transform to retain unfinished tokens.
After the composed body stream finishes, including the footer, the hook receives one final call with html: '' and isEnd: true. Its returned string is appended to the response; undefined or '' appends nothing. This also applies to buffered rendering (isStream: false). Hooks that ignore isEnd remain supported.
For example, using @lomray/consistent-suspense:
onResponse: ({ context: { appProps: { streamSuspense }, isStream }, html, isEnd }) => {
if (!isStream) return;
return isEnd ? streamSuspense.end() : streamSuspense.analyze(html);
},Middleware config
Production middleware can be configured without replacing the whole server pipeline:
export default entryServer(App, routes, {
middlewares: {
compression: {},
expressStatic: {
basename: '/static',
},
},
});Set either option to false when you want it disabled.
Logging
Use loggerProd and loggerDev to provide custom Vite-compatible loggers instead of the package default logger.
The default production logger is public API under @lomray/vite-ssr-boost/logger, so you can extend it instead of reimplementing the Vite Logger interface:
import type { LogErrorOptions } from 'vite';
import { Logger } from '@lomray/vite-ssr-boost/logger';
class JsonLogger extends Logger {
public error(msg: string, options?: LogErrorOptions): void {
console.error(JSON.stringify({ level: 'error', msg, stack: options?.error?.stack }));
}
}
export default entryServer(App, routes, { loggerProd: new JsonLogger({ logLevel: 2 }) });Constructor options: logLevel (1 errors, 2 warnings, 3 info, default 3) and logFilter(params), which returns true to drop a record. The same entry exports LogLevels, the ILoggerOptions and ILogParams types, and serializeErrors, which serializes React Router errors the way the built-in renderer does, without stack traces:
import { serializeErrors } from '@lomray/vite-ssr-boost/logger';Import from this entry rather than from services/* or helpers/*: those paths are internal layout and can move between releases.
