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. This is useful for logging, auth gates, hit counters, or anything that should fire on every request without per-page wiring. The counter below is live.
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: 3511
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 out: 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). Use them for warming caches, opening pools, registering metrics, or draining connections. They take no parameters and run only for their side effects.
> 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, and 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) See also Services · Public endpoints · Channels