ilusm.dev

mw

HTTP middleware - compose lists of fn(req, res, next) handlers, built-in middleware for logging, panic recovery (500), security headers, body size limits, timeouts, IP allow/block lists, HTTP Basic Auth, Bearer token auth, API key auth, in-memory sliding-window rate limiting, request ID injection, health check endpoint, response time header, content-type negotiation, HTTPS redirect, and a per-route circuit breaker.

Load with: use mw

What this module does

mw implements the standard HTTP middleware pattern. Each piece of middleware is a function fn(req, res, next) - it may inspect or mutate the request, call next(req, res) to continue the chain, or short-circuit by writing a response directly. mw.chain and mw.compose combine middleware lists into a single handler.

Quick example

use mw
use srv

# Build a middleware stack
handler = mw.chain([
    mw.log(),
    mw.recover(),
    mw.security(),
    mw.request_id(),
    mw.rate_limit(100, 60, \(req) srv.reqip(req)),
    mw.bearer(\(tok) verify_jwt(tok))
], \(req, res) srv.resjn(res, {ok: tru}))

# Register with server
srv.srvrt(app, "GET", "/api/data", handler)

Middleware

Composition

mwcha(mws, final) / mw.chain(mws, final)

Chains a list of middleware and a final handler into one fn(req, res).

mwcom(mws) / mw.compose(mws)

Combines a list of middleware into one fn(req, res, next) that can itself be used as middleware.

Logging and resilience

mwlog() / mw.log()

Logs METHOD path Nms via obs after each request.

mwrec() / mw.recover()

Wraps the chain in try. On error, sends a 500 and logs the error.

mwrt() / mw.response_time()

Adds an X-Response-Time: Nms header.

mwrid() / mw.request_id()

Generates a unique request ID, sets it on req.request_id and the X-Request-ID response header.

Security

mwsec() / mw.security()

Adds X-Content-Type-Options, X-Frame-Options, X-XSS-Protection, Strict-Transport-Security, and Referrer-Policy headers.

mwlim(max) / mw.limit(max)

Rejects requests whose body exceeds max bytes with a 400.

mwtmo(ms) / mw.timeout(ms)

Sends a 504 if the handler hasn't completed within ms milliseconds.

mwip(whitelist, blacklist) / mw.ip(wl, bl)

Blocks requests from IPs in blacklist or not in whitelist (either may be nil).

mwhtt() / mw.https()

Redirects HTTP requests to HTTPS with a 301.

Authentication

mwbas(creds) / mw.basic_auth(creds)

HTTP Basic Auth. creds is an object mapping username → password. Sets req.user on success.

mwbea(vfn) / mw.bearer(vfn)

Bearer token auth. Calls vfn(token); if truthy, sets req.user to the result.

mwkey(header, vfn) / mw.api_key(header, vfn)

API key auth from a named header. Sets req.api_key and req.api_user on success.

Rate limiting and circuit breaking

mwrat(max, window_s, keyfn) / mw.rate_limit(max, win, keyfn)

In-memory sliding-window rate limiter. keyfn(req) returns the key (e.g. client IP). Sends 429 when exceeded.

mwcir(name, threshold, timeout_ms) / mw.circuit_breaker(name, thresh, tmo)

Per-name circuit breaker. Opens after threshold failures and returns 503 until timeout_ms elapses.

Utilities

mwhea(path, cb) / mw.health(path, cb)

Intercepts requests to path and calls cb(). Returns 200 {status:"healthy"} or 503 {status:"unhealthy"}.

mwneg(default) / mw.negotiate(default)

Content negotiation. Sets req.accepts to "json", "html", "text", or the default based on the Accept header.

Notes

  • Requires trl, txt, srv, tim, obs, enc, and det.