Guides
Rouzer Integration
Rouzer re-exports alien-middleware as its middleware provider; this page keeps the import boundary, host mapping, and shared context rules explicit.
Rouzer's root API includes alien-middleware exports such as chain,
toFetchHandler, RequestContext, and RequestHandler. Rouzer routers are
middleware chains too, so route handlers and middleware can share one typed
request context.
Stay on the Rouzer Import Boundary
Import middleware helpers from rouzer when Rouzer owns your router, handler,
or client types.
import {
chain,
createRouter,
toFetchHandler,
type Middleware,
type RequestContext,
type RequestHandler,
type RouteRequestHandlerMap,
} from 'rouzer'
import type { HttpRouteTree } from 'rouzer/http'
This keeps route handlers, middleware chains, and adapter helpers using one
public type surface. Use direct alien-middleware imports when your application
owns the chain directly and does not need Rouzer-specific router types.
For larger application-stack patterns that are not Rouzer-specific, use Middleware Stacks.
Type Host Data Once
Create the top-level chain or router with the environment and runtime types that the adapter will provide.
type WorkerEnv = {
DATABASE_URL?: string
SESSION_SECRET?: string
}
const commonMiddleware = chain<WorkerEnv, {}, ExecutionContext>()
Then map the host's request arguments into host at the adapter boundary.
function createWorkerFetchHandler(handler: RequestHandler) {
return (request: Request, env: WorkerEnv, ctx: ExecutionContext) => {
const fetch = toFetchHandler(handler, {
host: {
env(name) {
return env[name as keyof WorkerEnv]
},
runtime: ctx,
waitUntil(promise) {
ctx.waitUntil(promise)
},
},
})
return fetch(request)
}
}
Middleware can then read environment values with context.env(name), runtime
data with context.host.runtime, and background work support with
context.waitUntil().
Compose Rouzer Middleware Before Routes
Put shared resources in a common chain before Rouzer route registration.
declare const dbMiddleware: Middleware
declare const i18nMiddleware: Middleware
declare const authMiddleware: Middleware
declare const apiRoutes: HttpRouteTree
declare const handlers: RouteRequestHandlerMap<typeof apiRoutes>
const commonMiddleware = chain<WorkerEnv, {}, ExecutionContext>()
.use(dbMiddleware)
.use(i18nMiddleware)
.use(authMiddleware)
const apiRouter = createRouter<WorkerEnv, {}, ExecutionContext>({
basePath: 'api/',
})
.use(commonMiddleware)
.use(apiRoutes, handlers)
Order the chain by dependency. If authMiddleware expects context.db, add
dbMiddleware first. If a feature middleware owns routes and context together,
make that feature router part of the chain before the final route tree.
Use Lazy Plugin Getters
For expensive request resources, return getters from middleware instead of opening resources eagerly.
type DbContext = {
db: WorkerDb
}
type WorkerDb = {
execute(sql: string): Promise<unknown>
}
declare function getOrCreateDb(databaseUrl: string): WorkerDb
const dbMiddleware = chain(
(context: RequestContext<WorkerEnv, {}, ExecutionContext>) => {
const databaseUrl = context.env('DATABASE_URL')
return {
get db(): WorkerDb {
if (!databaseUrl) {
throw new Error('DATABASE_URL is required to access context.db')
}
return getOrCreateDb(databaseUrl)
},
} satisfies DbContext
}
)
The getter is copied as a property descriptor, so downstream middleware reads
context.db like a normal property. This pattern keeps startup cheap and avoids
opening a database connection for requests that never need it.
Use closure variables for per-request memoization.
type Session = {
userId: string
}
declare function readSession(request: Request): Promise<Session | null>
const sessionMiddleware = chain().use(context => {
let session: Promise<Session | null> | undefined
return {
session() {
return (session ??= readSession(context.request))
},
}
})
Keep Adapter Error Boundaries Outside the Chain
Use the adapter wrapper for unexpected exceptions that should be reported and
converted into a generic 500 response.
declare function reportException(error: unknown, request: Request): void
function createSafeFetch(handler: RequestHandler) {
const fetch = toFetchHandler(handler)
return async (request: Request) => {
try {
return await fetch(request)
} catch (error) {
reportException(error, request)
return Response.json(
{ message: 'Internal server error' },
{ status: 500 }
)
}
}
}
Inside middleware and route handlers, return a Response for expected request
outcomes such as authentication failures, validation errors, or feature routes
that deliberately handle one path.
Share Host Access with Non-Request Work
If scheduled jobs or other host hooks need the same environment values as middleware, extract small helpers that accept an env reader instead of requiring a full request context.
type EnvReader<TEnv extends object> = {
env<TName extends keyof TEnv>(name: TName): TEnv[TName]
}
function getDatabaseUrl(context: EnvReader<WorkerEnv>) {
return context.env('DATABASE_URL')
}
Request middleware can pass the full request context. Scheduled jobs can pass a small object backed by the host's environment.
Rouzer may wear the space helmet, but alien-middleware is still the tiny pilot working the controls.