Beta documentation. This is an early preview — content is still in active development. Feedback helps shape the final release. Share your thoughts or join the discussion.

Request & Environment

Every handler receives a `ctx` (context) object. `.params` give you route parameters. For raw request data, use the `req` global.

On this page

Request data at a glance

Every handler receives a ctx (context) object. .params give you route parameters. For raw request data, use the req global.

app:get("/search", function(ctx)
    return {
        query = req.query.q,           -- ?q=hello
        method = req.method,           -- "GET"
        auth_header = req.headers["authorization"]  -- Bearer token
    }
end)

Reading query parameters

-- /search?q=lua&page=2&category=tutorial

app:get("/search", function(ctx)
    local q = req.query.q         -- "lua"
    local page = req.query.page   -- "2"
    -- req.get is the same as req.query
    return api.dataset.find("articles", {
        filter = { title = { like = "%" .. q .. "%" } }
    })
end)

Reading form data (POST)

-- Form: <input name="email"> <input name="password">

app:post("/login", function(ctx)
    local email = req.post.email
    local password = req.post.password
    local user = api.users.login(email, password)
    if not user then
        ctx:flash("error", "Invalid credentials")
        return ctx:redirect("/login")
    end
    return ctx:redirect("/dashboard")
end)

Reading JSON body

Enable the "json" middleware, then use ctx.body:

app:use("json")

app:post("/api/users", function(ctx)
    -- ctx.body is now a Lua table (parsed JSON)
    local name = ctx.body.name
    local email = ctx.body.email
    return api.users.create({ username = name, email = email, password = "temp" })
end)

Reading raw body

app:post("/webhook", function(ctx)
    local raw = req.body           -- raw string
    local signature = req.headers["x-signature"]
    -- verify signature...
end, { csrf = false })

Request headers

All header names are lowercased and use hyphens:

req.headers["content-type"]      -- ✓ correct
req.headers["Content-Type"]      -- ✗ wrong (not lowercased)
req.headers["content_type"]      -- ✗ wrong (underscore)

File uploads

app:post("/upload", function(ctx)
    local file = req.files.photo
    if file then
        -- file.name, file.size, file.type, file.tmp_name
        api.files.upload({ file = file })
    end
end)

Reading cookies

app:get("/preferences", function(ctx)
    local theme = req.cookie.theme or "light"
    return { theme = theme }
end)

Session data

Session is read-only. Access user state via env.is_user instead:

if env.is_user then
    local me = api.users.me()
    return { user = me }
end

Environment info

app:get("/info", function(ctx)
    return {
        site = env.sitename,        -- your site name
        site_id = env.siteid,       -- numeric ID
        ip = env.remote_ip,         -- visitor IP
        is_admin = env.is_admin,    -- true/false
        timezone = env.timezone     -- server timezone
    }
end)

Next: Responses — send data back to the browser.

Previous Routing Next Responses