Web & APIs
Middleware
A pipeline composes cross-cutting concerns
around every route in a web block — error handling, CORS, rate limiting,
logging, and more — as ordered, readable English steps. No decorators, no
framework plumbing.
The pipeline block
Declare a pipeline … end inside a web block. The first step is
outermost: a request flows in through the steps, reaches
the route, and the response flows back out through them in reverse.
controller Orders at "/orders"
on GET "/"
give back ok message "3 open orders"
end
end
web OrderApp
pipeline
catch errors
allow any origin
add correlation id
limit 100 requests per minute
limit request size to 64 kilobytes
log requests
compress responses
end
use Orders
end
publish OrderApp on port 5000
Built-in steps
| Step | Effect |
|---|---|
catch errors | A failing route answers 500 instead of crashing the host. |
allow origins "…" / allow any origin | CORS: sets Access-Control-Allow-Origin. |
add correlation id | Reuse or generate an X-Correlation-Id per request. |
limit N requests per second|minute|hour | Fixed-window rate limit — 429 when exceeded (per client). |
limit request size to N kilobytes | Reject an over-large body with 413. |
log requests | A structured record per request into the host's log sink. |
compress responses | Real gzip compression (with Vary) when the client accepts it. |
Steps run top-to-bottom on the way in. Put catch errors first so it wraps everything, and rate/size limits before the expensive work they protect.
Custom steps
Declare your own step with step Name with request … end. Call
next(request) to run the rest of the pipeline (and the route),
then do something with the response before returning it — the classic
wrap-around shape:
step Timing
with request
create response = next(request)
# ... measure, add a header, log timing ...
give back response
end
Add it to the pipeline like any built-in step, with use Timing:
pipeline
catch errors
use Timing
end
next(request) is an injected value inside a step, not a keyword. Calling it once and returning its result is the common case; you can inspect or replace the request before, and the response after.
Diagnostics are ZX2700–ZX2799. Next: seeing inside a running service — Observability.