Middleware
A middleware is a service with two extras: a route pattern (match: '/...') and lifecycle hooks (before, after, on_start[], on_shutdown[]). When a request matches, the hooks run around the page handler — useful for logging, auth gates, hit counters, anything that should fire on every request without per-page wiring. The counter below is a live one.
Live: a page-view counter
main.rat — fires after every request
The page_views service matches every URL ('/**') and bumps total in its after hook. The number below reads the live state; every page you visit on this site — including this refresh — adds one.
# main.rat
> service [page_views]
match: '/**'
total: 0
after[req]
total << total + 1
# any page
> page
<p> This site has been viewed [page_views.total] times. Total page views so far: 357
Refresh this page or visit any other doc to bump the counter.
Short-circuit with before
Auth gate on /admin
A before hook runs before the page handler. Assign to redirect or abort to bail — the page handler never runs and the user sees the alternative response. Pair with match to scope the gate to a subtree.
> service [admin_gate]
match: '/admin/**'
before[req]
(is not session.user_id)
redirect << '/login'
# or for an API endpoint:
> service [api_auth]
match: '/api/**'
before[req]
(is not session.user_id)
abort << {status: 401, body: 'unauthorized'} Lifecycle: on_start / on_shutdown
Boot-time and drain-time work
on_start[] runs once when the server boots; on_shutdown[] runs once at SIGINT/SIGTERM (the framework calls http.Server.Shutdown after the hook returns). Useful for warming caches, opening pools, registering metrics, draining connections. They take no parameters — they're side-effect surfaces.
> service [warmup]
ready: false
on_start[]
log('booting')
ready << true
on_shutdown[]
log('draining connections') Per-request hooks
on_request_start / on_request_end fire on every URL
When you don't need URL matching but want a hook on every request — request tracing, structured access logs, request-scoped resources — declare on_request_start[] and on_request_end[]. They fire on every URL regardless of match:.
> service [tracer]
on_request_start[]
log('begin')
on_request_end[]
log('end') Match patterns
Glob over the route
match is a glob over the request path. '/**' matches every route; '/api/**' matches every API call; '/admin' matches only the literal path; '/users/*/posts' uses single-segment wildcards. Multiple middlewares can match the same request — they fire in alphabetic-name order for before, reverse for after.
> service [api_log]
match: '/api/**'
hits: 0
after[req]
hits << hits + 1
log(req.method, req.url)
> service [request_log]
match: '/**'
after[req]
log('served:', req.url) That closes the advanced section. Head back to the docs index or the About page for context on how this site is built.