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.

Responses

Whatever your handler returns, Wapka figures out the right format:

On this page

The simple rule

Whatever your handler returns, Wapka figures out the right format:

You return this Wapka sends
A Lua table { a = 1 } application/json with {"a":1}
A string "Hello" text/html with Hello
A number 404 That HTTP status code

That's all you need for 80% of routes. For the other 20%, use the helpers below.

Explicit content types

ctx:json(data)

Force JSON even if your data looks like a string:

app:get("/api/status", function(ctx)
    return ctx:json({ ok = true, time = os.date() })
end)

ctx:html(str)

Send raw HTML:

app:get("/terms", function(ctx)
    return ctx:html("<h1>Terms of Service</h1><p>...</p>")
end)

ctx:text(str)

Send plain text — no HTML interpretation:

app:get("/robots.txt", function(ctx)
    return ctx:text("User-agent: *\nAllow: /")
end)

HTTP status codes

ctx:status(code)

Set the status code. Chainable — you can call .status() then return data:

app:post("/users", function(ctx)
    local user = api.users.create(ctx.body)
    return ctx:status(201):json(user)
end)

Per-route with a number return

A bare number sets the status:

app:delete("/users/:id", function(ctx)
    api.users.delete(tonumber(ctx.params.id))
    return 204
end)

ctx:ok(data)

Pass data through unchanged. Useful when a helper already formatted the response:

app:get("/cache", function(ctx)
    local cached = getFromCache()
    if cached then
        return ctx:ok(cached)
    end
    return { fresh = true }
end)

Error responses

ctx:error(message, code)

Sends a structured error. Always returns { _error = true, message = "...", status = code }:

app:get("/users/:id", function(ctx)
    local user = api.users.get(tonumber(ctx.params.id))
    if not user then
        return ctx:error("User not found", 404)
    end
    return user
end)

Redirects

ctx:redirect(url, code)

app:post("/logout", function(ctx)
    -- clear session...
    return ctx:redirect("/")
end)

app:get("/old-page", function(ctx)
    return ctx:redirect("/new-page", 301)    -- permanent redirect
end)

Default status is 302 (temporary).

Setting response headers

ctx:set_header(name, value)

Chainable. Call before returning:

app:get("/data.csv", function(ctx)
    ctx:set_header("Content-Type", "text/csv")
    ctx:set_header("Cache-Control", "public, max-age=3600")
    return "name,email\nJohn,john@example.com"
end)

Setting cookies

-- Set a cookie that expires in 7 days
ctx:set_cookie("theme", "dark", { max_age = 604800 })

-- Read
local theme = ctx:get_cookie("theme") or "light"

-- Check existence
if ctx:has_cookie("returning_user") then
    -- welcome back
end

-- Delete
ctx:delete_cookie("session_temp")

Common response patterns

-- Creation: 201 with the new object
return ctx:status(201):json(user)

-- Deletion: 204 no content
return 204

-- Validation error: 422 with message
return ctx:error("Email is required", 422)

-- Not found: 404
return ctx:error("Post not found", 404)

-- Unauthorized: 401
return ctx:error("Please log in", 401)

Next: Middleware — run code before your handlers.

Previous Request & Environment Next Middleware