Context & Requests

Every route handler receives a context object containing request data, response helpers, and shared state via locals.

Context Structure

ctx = {
    request = {          -- Incoming request data
        method = "GET",  -- HTTP method
        path = "/users",  -- Request path
        query = {},       -- Query parameters
        headers = {},     -- Request headers
        body = {}         -- Parsed body data
    },
    params = {},         -- Route parameters (e.g., :id)
    locals = {},         -- Shared state (middleware storage)
    response = {         -- Response helpers
        setHeader = function(name, value),
        setStatus = function(code)
    }
}

Request Object

Access incoming request data via ctx.request:

Basic Properties

app:get("/example", function(ctx)
    -- HTTP method (GET, POST, PUT, DELETE, etc.)
    local method = ctx.request.method
    
    -- Full request path
    local path = ctx.request.path
    
    -- Query string parameters
    local search = ctx.request.query.search
    local page = ctx.request.query.page or "1"
    
    return Nixi.p(nil, "Method: " .. method .. ", Path: " .. path)
end)

Headers

app:get("/api/user", function(ctx)
    -- Access specific header
    local auth = ctx.request.headers["Authorization"]
    local content_type = ctx.request.headers["Content-Type"]
    local user_agent = ctx.request.headers["User-Agent"]
    
    -- Check for API key
    if not auth then
        return Nixi.error(401, "Missing authorization")
    end
    
    return Nixi.json({ authorized = true })
end)

Body Parsing

app:post("/form", function(ctx)
    -- Access parsed form data
    local name = ctx.request.body.name
    local email = ctx.request.body.email
    
    -- For multipart forms
    local file = ctx.request.body.file
    if file then
        local filename = file.filename
        local content = file.content
    end
    
    return Nixi.p(nil, "Hello " .. name)
end)

app:post("/api/data", function(ctx)
    -- JSON body is auto-parsed
    local data = ctx.request.body
    local username = data.username
    local settings = data.settings
    
    return Nixi.json({ received = true })
end)

Route Parameters

-- Define route with parameters
app:get("/users/:id/posts/:post_id", function(ctx)
    local user_id = ctx.params.id
    local post_id = ctx.params.post_id
    
    return Nixi.p(nil, "User " .. user_id .. ", Post " .. post_id)
end)

Response Object

Modify the response using ctx.response:

app:get("/custom", function(ctx)
    -- Set custom headers
    ctx.response:setHeader("X-Custom-Header", "value")
    ctx.response:setHeader("Cache-Control", "no-cache")
    
    -- Set status code
    ctx.response:setStatus(201)
    
    return "Created!"
end)

Locals Storage

Use ctx.locals to share data between middleware and route handlers:

Setting Locals in Middleware

-- Authentication middleware
local function authenticate(ctx)
    local token = ctx.request.headers["Authorization"]
    
    if token then
        -- Validate token and set user info
        local user = validateToken(token)
        ctx.locals.user = user
        ctx.locals.isAuthenticated = true
    else
        ctx.locals.isAuthenticated = false
    end
end

-- Apply middleware
app:use(authenticate)

Accessing Locals in Routes

app:get("/profile", function(ctx)
    if not ctx.locals.isAuthenticated then
        return Nixi.redirect("/login")
    end
    
    local user = ctx.locals.user
    return Nixi.p(nil, "Welcome, " .. user.name)
end)

-- Protected API endpoint
app:get("/api/data", function(ctx)
    if not ctx.locals.user then
        return Nixi.error(403, "Access denied")
    end
    
    return Nixi.json({
        user = ctx.locals.user.name,
        data = getData(ctx.locals.user.id)
    })
end)

Common Locals Patterns

Use Case Locals Key Set By
User session ctx.locals.user Auth middleware
Database connection ctx.locals.db DB middleware
Request timing ctx.locals.startTime Timing middleware
Flash messages ctx.locals.flash Session middleware

Example: Full Middleware Chain

-- Request timing middleware
local function timing(ctx)
    ctx.locals.startTime = os.time()
end

-- Authentication middleware
local function auth(ctx)
    local token = ctx.request.headers["Authorization"]
    if token then
        ctx.locals.user = decodeJWT(token)
        ctx.locals.isAuthenticated = true
    end
end

-- Logger middleware
local function logger(ctx)
    local elapsed = os.time() - ctx.locals.startTime
    print(string.format("[%s] %s - %dms", 
        ctx.request.method, 
        ctx.request.path, 
        elapsed * 1000))
end

-- Apply middleware (order matters!)
app:use(timing)
app:use(auth)

-- Log after request
app:get("/slow", function(ctx)
    -- ... route handler ...
end)

Context in Components

Pass context to child components:

app:get("/dashboard", function(ctx)
    return Nixi.div(
        nil,
        Nixi.h1(nil, "Dashboard"),
        Nixi.Component("user_panel", {
            user = ctx.locals.user,
            ctx = ctx
        })
    )
end)