Tour: Middleware
Files named _middleware.rex run before every handler in their directory
and all subdirectories. They execute root-first (most general to most specific).
Middleware Chain for This Page
When you requested /tour/middleware, the server ran:
routes/_middleware.rex— security headers, request ID, view-source tool- Then this handler (
routes/tour/middleware.rex)
Check the response headers — you'll see X-Request-Id and X-Powered-By: rex-serve
added by the global middleware.
Short-Circuit
If middleware sets res.status to 400+, the chain stops and the handler never runs.
This is how auth works:
unless api-key do res.status = 401 {ok: false, error: "unauthorized"} end
Try hitting the API without credentials: curl http://localhost:4000/api/articles
Data Passing
Variables set by middleware persist into the handler. The API middleware sets
principal = api-key, which downstream handlers can read.
View-Source: A Middleware Feature
The view-source tool is implemented entirely in the global middleware. Add
X-View-Source: 1 to any request to see the Rex source:
Try: View source for this page
curl -H 'X-View-Source: 1' http://localhost:4000/tour/middleware
Global Middleware Source
/* Global middleware: security headers, request ID, and view-source tool. This runs for EVERY request — static files, API, and pages alike. */ request-id = headers.x-request-id or time.uuid() res.headers.x-request-id = request-id res.headers.x-content-type-options = "nosniff" res.headers.x-powered-by = "rex-serve" /* View-source tool: send X-View-Source header to see the .rex source for the current route. This is opt-in per project (via this middleware). */ when headers.x-view-source do /* Map the URL path back to a source file */source-path = "routes" + path + ".rex" source = fs.read(source-path) /* Try index.rex for directory paths */ unless source do source-path = "routes" + path + "/index.rex" source = fs.read(source-path) end /* Try [slug].rex pattern — read the directory for any bracket file */ when source do res.status = 418 res.headers.content-type = "text/plain; charset=utf-8" res.headers.x-source-path = source-path "// Source: " + source-path + " " + source end end
API Middleware Source
/* API middleware: key-based authentication. Protects all /api/* routes. A valid key must exist in the database. Seed one with: sqlite3 examples/knowledge-base/data.db "INSERT INTO kv VALUES('keys:demo','1')" */ api-key = headers.authorization unless api-key do res.status = 401 return { ok: false error: "missing_api_key" hint: "Add Authorization header. Seed a key: sqlite3 data.db \"INSERT INTO kv VALUES('keys:demo','1')\"" } end key-valid = db.get("keys:" + api-key) unless key-valid do key-valid res.status = 401 return { ok: false error: "invalid_api_key" } end key-valid /* Authenticated identity — available to downstream handlers */ log.info(`authenticated: ${api-key}`)
This Page's Source
/* Tour Stop 3: Middleware */ res.headers.content-type = "text/html; charset=utf-8" layout = fs.read("routes/_layouts/page.html") unless layout do status = 500 return "layout not found" end /* Read the actual middleware source to display */ global-mw = fs.read("routes/_middleware.rex") api-mw = fs.read("routes/api/_middleware.rex") self-source = fs.read("routes/tour/middleware.rex") hl-global = when global-mw do html.raw(html.highlight(global-mw)) end hl-api = when api-mw do html.raw(html.highlight(api-mw)) end hl-self = when self-source do html.raw(html.highlight(self-source)) end auth-snippet = "unless api-key do res.status = 401 {ok: false, error: \"unauthorized\"} end" body = html`<h1>Tour: Middleware</h1> <p class="source-link"><a href="/tour/templates">Next: Templates →</a></p> <p>Files named <code>_middleware.rex</code> run before every handler in their directory and all subdirectories. They execute root-first (most general to most specific).</p> <h2>Middleware Chain for This Page</h2> <p>When you requested <code>/tour/middleware</code>, the server ran:</p> <ol> <li><code>routes/_middleware.rex</code> — security headers, request ID, view-source tool</li> <li><em>Then</em> this handler (<code>routes/tour/middleware.rex</code>)</li> </ol> <p>Check the response headers — you'll see <code>X-Request-Id</code> and <code>X-Powered-By: rex-serve</code> added by the global middleware.</p> <h2>Short-Circuit</h2> <p>If middleware sets <code>res.status</code> to 400+, the chain stops and the handler never runs. This is how auth works:</p> <pre>${html.raw(html.highlight(auth-snippet))}</pre> <p>Try hitting the API without credentials: <code>curl http://localhost:4000/api/articles</code></p> <h2>Data Passing</h2> <p>Variables set by middleware persist into the handler. The API middleware sets <code>principal = api-key</code>, which downstream handlers can read.</p> <h2>View-Source: A Middleware Feature</h2> <p>The view-source tool is implemented entirely in the global middleware. Add <code>X-View-Source: 1</code> to any request to see the Rex source:</p> <details class="try-it"> <summary>Try: View source for this page</summary> <pre>curl -H 'X-View-Source: 1' http://localhost:4000/tour/middleware</pre> </details> <h2>Global Middleware Source</h2> <pre>${hl-global}</pre> <h2>API Middleware Source</h2> <pre>${hl-api}</pre> <h2>This Page's Source</h2> <pre>${hl-self}</pre>` template.render(layout, { title: "Middleware" body: body footer: "<a href='/tour/routing'>← Routing</a> · <a href='/tour/templates'>Templates →</a>" })