Middleware and Security
Use middleware and security with the current published Askr packages.
@askrjs/server/middleware0.0.3
How to use middleware and security
Install middleware in composition order: request identity and telemetry first, security and domain middleware next, terminal errors last.
- Import the published @askrjs/server/middleware entrypoint.
- Keep middleware and security configuration next to the component, route, or server composition root that owns it.
- Handle pending, unavailable, cancellation, and failure states, then verify the production path.
const router = createRouter()
.use(requestIds(), telemetry(), securityHeaders())
.use(authMiddleware(auth))
.get('/projects', listProjects);Middleware order
A Middleware is (ctx, next) => Response | Promise<Response> — call next() to continue down the chain, or return your own Response to short-circuit it. Middleware registered with router.use(...) runs in the order it's added, and because each layer wraps the next one, code written before next() runs on the way in and code written after it runs on the way out, once the inner handler has produced a response. Route-level middleware from ApiRouteOptions.middleware runs closer to the handler than anything registered on the router itself.
Security headers
securityHeaders(options) sets response headers like Content-Security-Policy, Referrer-Policy, and X-Frame-Options, each overridable through the contentSecurityPolicy, referrerPolicy, and frameOptions options rather than hardcoded. cors(options) handles the CORS handshake separately — origin can be a fixed string or a function of the request origin and context, and methods, allowedHeaders, exposedHeaders, credentials, and maxAgeSeconds all map to the corresponding response headers. enforceHttps() is a third, narrower piece of the same family that redirects plain HTTP traffic to HTTPS.
Cookies and redirects
ctx.setCookie(response, name, value, options) and ctx.clearCookie(response, name, options) both take a Response and return a new one with the Set-Cookie header attached — CookieOptions covers domain, path, expires, maxAge, httpOnly, secure, and sameSite ("strict" | "lax" | "none"). Per the README, these only modify the response; they don't establish a session or check a password, so pairing setCookie() with an actual auth check is on the caller. ctx.redirect(location, status?) defaults to 302 but accepts 301, 303, 307, or 308, and ctx.challenge(options) is the companion for auth failures — it sets WWW-Authenticate with a configurable scheme and realm and returns 401 by default, or 407 on request.
Error handling
ServerAppOptions.onError(error, context) is the single place an unhandled exception from any handler or middleware ends up — return a Response from it and that's what the client receives, instead of the framework's default. Because ctx.problem() and its status-helper wrappers are available on the context passed into onError, a typical implementation just logs the error and returns ctx.internalServerError() or a custom problem+json body. Nothing about this hook cares whether routes were registered through defineRoutes() or a Router — both ultimately produce ApiRoute entries the app dispatches the same way.