createHandler options
You rarely call createHandler yourself: the generated server entry does, with the options of src/entry-server.ts.
You rarely call createHandler yourself: the generated server entry does, with the options of src/entry-server.ts.
The ones you will reach for first:
| Option | Purpose |
|---|---|
security.allowedHosts |
The host names the server answers for. Set it in production |
security.csp, security.headers, security.csrf, security.limits, security.trustProxy, security.cookieSecrets |
The security settings |
middleware |
(event, next) => Response functions around every request |
env |
A defineEnv schema, validated at startup |
mode |
"stream" (default), "async" or "string" |
onError, onWarning |
Where unexpected errors and configuration warnings go (default: console.error / console.warn) |
document |
{ lang, rootId, head } of the generated HTML document |
trailingSlash, trustedOrigins, healthPath, rpc, maxPayloadBytes |
Routing and limit details |
features |
The features the handler has (pages(), rpc(), ...; see below). The generated entry passes the ones the app uses |
runMiddlewareForStaticFiles |
true runs the global middleware for public/ and build files before they are sent (default false; see security) |
Features: pay for what the handler uses#
A handler has only the request pipeline until you list the features it needs. Each feature is an object from its own
subpath. The generated server entry lists the ones the app uses: routing() when dusk.config.ts declares redirects,
rewrites, headers or route rules, isr() when a page or a route revalidates, draftMode() when the configuration sets
draft, and the server functions, API routes and streams when the app has them. An entry file (src/entry-server.ts) can
set any option, so an app with one gets every option feature. A handler you write yourself lists only what it serves:
import { apiRoutes } from "@shadow-js/dusk/features/api-routes";
import { createHandler, type DuskRuntimeManifest } from "@shadow-js/dusk/server";
declare const manifest: DuskRuntimeManifest; // the manifest the build writes
// An API only: no page rendering, server functions, cache, streams, images or draft mode is in the bundle.
export const handler = createHandler({ manifest, features: [apiRoutes()] });
| Feature | Adds | Needs |
|---|---|---|
pages() |
Page rendering, the document shell, the SPA shell (renderShell), the navigation gate |
nothing |
rpc() |
Server functions (/_dusk/fn/), the batch endpoint |
nothing (single-flight answers need pages()) |
isr() |
The page cache: revalidate, revalidatePath, revalidateTag, the cache option |
pages() |
dataCache() |
cacheData and cachedFetch |
nothing |
apiRoutes() |
API routes (app/api) |
nothing |
streams() |
SSE and WebSocket routes, Handler.upgrade |
nothing |
images() |
The image optimizer at /_dusk/image |
nothing |
draftMode() |
The draft endpoints and the draft cookie, and the draft option |
nothing |
routing() |
The routing option: redirects, rewrites, headers and route rules, validated when the handler is created |
nothing |
A handler that asks for something it does not have is refused when it is created, with the factory to add: a page
route without pages(), a cache option without isr(), a routing option without routing(), and so on. An option's
validation runs in its feature, so a handler without the feature does not load the validator. src/entry-server.ts can list its own features
too; the list replaces the generated default.
pages() also needs the router features its routes' exports use. A route that exports loader needs loaderCache() from @shadow-js/router/loaders,
head needs headManagement() from @shadow-js/router/head, error needs errorBoundaries() from @shadow-js/router/errors,
matchFilters needs matchFilters() from @shadow-js/router/filters, validateSearch needs searchValidation() from
@shadow-js/router/search, notFound needs notFoundUi() from @shadow-js/router/not-found, and pending needs pendingUi() from
@shadow-js/router/pending. A generated entry (dusk build) reads those exports and passes the features for you. A handler you write
lists them in pages({ routerFeatures }), and pages() refuses a route whose feature is missing, naming the route, the export and the factory to add:
import { headManagement } from "@shadow-js/router/head";
import { loaderCache } from "@shadow-js/router/loaders";
import { pages } from "@shadow-js/dusk/features/pages";
import { createHandler, type DuskRuntimeManifest } from "@shadow-js/dusk/server";
declare const manifest: DuskRuntimeManifest; // a manifest whose page routes export loader and head
export const handler = createHandler({
manifest,
features: [pages({ routerFeatures: [loaderCache(), headManagement()] })],
});