How a request runs
Each request builds a fresh Lua environment, runs your handler, and discards it.
One environment per request
Nothing survives in memory between requests.
local seen = 0
on "fetch" (function(request)
seen = seen + 1
return { body = tostring(seen) } -- always "1"
end)
Each request gets its own copy of the script's state. Nothing is shared between requests, and nothing needs cleaning up after one finishes.
Compiled code is shared, and the environment is built ahead: the
platform keeps a few fresh ones per revision, each having run the top
level once and served nothing, so a request takes one rather than
building it. A top-level value such as os.time() or a secret was
read when the environment was built, at most 30 seconds earlier.
Anything you need to keep goes in kv, a database, or an object.
The top level runs once
Code outside your handler runs when the environment is built. That is where declarations go:
local visits = kv "visits"
local db = database "catalog" { migrations = "migrations/catalog" }
on "fetch" (function(request)
-- handler body
end)
Declarations are only valid at the top level. To collect them the platform runs your entry point with every effectful surface stubbed out, so it derives the contract without executing a single handler.
Request and response
The handler takes a request table and returns a response table.
on "fetch" (function(request)
if request.method == "POST" and request.path == "/api/echo" then
return {
status_code = 200,
headers = { ["content-type"] = "application/json" },
body = request.body,
}
end
return { status_code = 404, body = "no such route" }
end)
request.path is relative to your script: one served at /hello/
sees /api/echo, not /hello/api/echo. request.query is a decoded
table. Every field is in the http reference.
Budget
A request is bounded by work, not by time: waiting on io costs nothing, computing costs. The ceiling is about a second of solid computation, which no ordinary handler comes near.
| Bound | Value | Covers |
|---|---|---|
| work budget | 5,000,000 work units | computation in the vm |
| wall backstop | 10 seconds | code stuck inside a host call that never returns to the vm |
| request deadline | 30 seconds | the whole request, waiting included |
| request body | 10 MB | larger bodies are refused with 413 |
Work that does not fit belongs in a queue or a workflow.
Gotchas
- Top-level state looks like a cache and is not one. The counter above resets on every request, including in production.
- Work at the top level is paid on every request, since the environment is rebuilt each time. Put it in the handler.
kv "visits"inside a handler is an error, not a late declaration. The declaration forms are top level only.