Lua API Reference
Interface documentation: what goes in, what comes out.
Handler Interface
Input: Request table Output: Response table
function handler_name(req)
-- Process request
return {
status = 200,
body = "response body",
headers = {["Content-Type"] = "text/plain"}
}
end
Request Table (Input)
req = {
method = "GET", -- HTTP method string
path = "/users/123", -- URL path string
url = "http://...", -- Full URL string
host = "example.com", -- Host header string
remote_addr = "192.168.1.1:12345", -- Client IP:port string
headers = { -- Map of header name → value
["Content-Type"] = "application/json",
["Authorization"] = "Bearer token"
},
params = { -- Chi URL parameters
id = "123" -- From pattern /users/{id}
},
query = { -- Query string parameters
foo = "bar", -- From ?foo=bar&limit=10
limit = "10"
},
body = "raw request body" -- String (max 10MB, cached)
}
All fields are strings or tables of strings.
Response Table (Output)
return {
status = 200, -- HTTP status code (integer)
body = "response content", -- Response body (string)
headers = { -- Optional response headers
["Content-Type"] = "application/json",
["Cache-Control"] = "no-cache"
}
}
Required: status and body Optional: headers
Default Values
If you don’t provide:
status: defaults to200headers: defaults to{}
Middleware Interface
Input: Request table + next callback Output: Response table OR nil
function middleware_name(req, next)
-- Option 1: Short-circuit (return response)
if not req.headers["Authorization"] then
return {
status = 401,
body = "Unauthorized"
}
end
-- Option 2: Continue chain
next()
return nil -- Must return nil to continue
end
When to Return What
| Return Value | Behavior |
|---|---|
Response table {status, body, headers} | Stop chain, send response |
nil | Continue chain to next middleware/handler |
Critical: Call next() before returning nil.
Go Primitive
log(message)
Input: String message Output: None (void)
log("User authenticated: user_id=" .. user_id)
log("Error: " .. err)
Writes to structured logger. Use for debugging and auditing.
Error Handler Interface
Input: Request table Output: Response table
function handle_404(req)
return {
status = 404,
body = '{"error": "Not Found", "path": "' .. req.path .. '"}',
headers = {["Content-Type"] = "application/json"}
}
end
function handle_405(req)
return {
status = 405,
body = '{"error": "Method Not Allowed"}',
headers = {["Content-Type"] = "application/json"}
}
end
Same as handler interface but called automatically on HTTP errors.
Complete Examples
Simple Handler
function get_status(req)
return {
status = 200,
body = "OK",
headers = {["Content-Type"] = "text/plain"}
}
end
Config:
routes:
- method: "GET"
pattern: "/status"
handler: "get_status"
Authentication Middleware
function require_auth(req, next)
local token = req.headers["Authorization"]
if not token or token == "" then
log("Missing authorization header")
return {
status = 401,
body = '{"error": "Unauthorized"}',
headers = {["Content-Type"] = "application/json"}
}
end
-- Basic token validation (check prefix)
if not token:match("^Bearer ") then
log("Invalid token format")
return {
status = 401,
body = '{"error": "Invalid token format"}'
}
end
-- Token valid, continue
next()
return nil
end
Config:
routes:
- method: "POST"
pattern: "/api/data"
handler: "process_data"
middleware:
- "require_auth"
Logging Middleware
function log_requests(req, next)
log(req.method .. " " .. req.path .. " from " .. req.remote_addr)
next()
return nil
end
Config:
route_groups:
- pattern: "/api"
middleware:
- "log_requests"
routes:
# All /api/* routes get logged
Type Reference
Request Table Fields
| Field | Type | Example | Always Present |
|---|---|---|---|
method | string | "GET" | ✅ |
path | string | "/users/123" | ✅ |
url | string | "http://..." | ✅ |
host | string | "example.com" | ✅ |
remote_addr | string | "192.168.1.1:12345" | ✅ |
headers | table | {["Content-Type"] = "..."} | ✅ (may be empty) |
params | table | {id = "123"} | ✅ (may be empty) |
query | table | {foo = "bar"} | ✅ (may be empty) |
body | string | "..." | ✅ (may be empty) |
Response Table Fields
| Field | Type | Required | Example |
|---|---|---|---|
status | integer | ✅ | 200 |
body | string | ✅ | "Hello" |
headers | table | ❌ | {["Content-Type"] = "text/plain"} |
HTTP Response Table Fields
| Field | Type | Always Present |
|---|---|---|
status | integer | ✅ |
body | string | ✅ |
headers | table | ✅ (may be empty) |
Common Patterns
JSON Response
function json_handler(req)
local data = {
success = true,
message = "Data processed"
}
return {
status = 200,
body = encode_json(data), -- Your JSON encoding function
headers = {["Content-Type"] = "application/json"}
}
end
Conditional Middleware
function api_only_middleware(req, next)
-- Only apply to /api/* paths
if not req.path:match("^/api/") then
next()
return nil
end
-- API-specific logic here
log("API request: " .. req.path)
next()
return nil
end
Best Practices
- Set Content-Type header for all responses
- Log errors for debugging
- Return early on validation failures
- Use nil return in middleware to continue chain
- Don’t forget to call
next()before returning nil
Performance Notes
- Request body cached: Reading
req.bodymultiple times is free - String operations: Lua string concat (
..) is efficient for small strings - Table construction: Pre-allocated internally for request tables