Routing
Routing is the foundation of any Nixi application. Routes map URL paths to handler functions that process requests and return responses.
Basic Routes
HTTP Methods
Nixi supports all standard HTTP methods:
-- GET request (default for browsing)
app:get("/path", handler)
-- POST request (form submissions, API calls)
app:post("/path", handler)
-- PUT request (updates)
app:put("/path", handler)
-- DELETE request
app:delete("/path", handler)
Dynamic Routes
Use parameters in your paths with the :param syntax:
-- User profile route
app:get("/users/:id", function(ctx)
local user_id = ctx.params.id
return Nixi.p(nil, "User ID: " .. user_id)
end)
-- Blog post route
app:get("/blog/:year/:month/:slug", function(ctx)
local year = ctx.params.year
local month = ctx.params.month
local slug = ctx.params.slug
return Nixi.p(nil, year .. "/" .. month .. "/" .. slug)
end)
Query Parameters
Access query string parameters via ctx.request.query:
-- URL: /search?q=lua&page=2
app:get("/search", function(ctx)
local query = ctx.request.query.q
local page = ctx.request.query.page or "1"
return Nixi.p(nil, "Searching for: " .. query .. " on page " .. page)
end)
Route Handler
The handler function receives a context object and returns content.
Handler Return Types
app:get("/example", function(ctx)
-- Return a string (auto-wrapped in response)
return "Simple text response"
-- Or return a response table for more control
return {
status = 200,
headers = { ["Content-Type"] = "text/plain" },
body = "Custom response"
}
end)
Middleware
Middleware functions process requests before they reach your route handler:
-- Logger middleware
local function logger(ctx)
print(string.format("[%s] %s", ctx.request.method, ctx.request.path))
end
-- Authentication middleware
local function require_auth(ctx)
local token = ctx.request.headers["Authorization"]
if not token then
return {
status = 401,
body = "Unauthorized"
}
end
end
-- Apply middleware to all routes
app:use(logger)
app:use(require_auth)
Response Helpers
JSON Response
app:get("/api/user", function(ctx)
return Nixi.json({
name = "John",
email = "john@example.com",
age = 30
})
end)
Redirect
app:get("/old-page", function(ctx)
return Nixi.redirect("/new-page")
end)
Error Response
app:get("/protected", function(ctx)
local authorized = false
if not authorized then
return Nixi.error(403, "Access denied")
end
return "Secret content"
end)
Route Matching
| Pattern | Matches | Example |
|---|---|---|
/ |
Exact path | / |
/about |
Exact path | /about |
/:id |
Single segment | /123, /abc |
/:category/:id |
Multiple segments | /users/123 |
File-Based Routing
Nixi supports automatic route discovery from the routes/ directory.
This is similar to Next.js file-based routing.
Project Structure
my-app/
├── app.lua # Main application
├── routes/ # File-based routes (auto-discovered)
│ ├── index.lua # → /
│ ├── about.lua # → /about
│ ├── users.lua # → /users
│ └── users/
│ └── [id].lua # → /users/:id
├── layouts/ # Layout templates
│ └── default.lua
└── server.lua # Development server
Route File Format
Route files return a table with the route configuration:
-- routes/index.lua
return {
methods = { "GET" },
name = "home",
layout = "default",
handler = function(ctx)
return Nixi.html([[
<div class="container">
<h1>Welcome!</h1>
</div>
]])
end,
}
Dynamic Routes
Use brackets [] for dynamic segments:
-- routes/users/[id].lua
return {
methods = { "GET" },
name = "users.show",
handler = function(ctx)
local user_id = ctx.params.id
return Nixi.html("User ID: " .. user_id)
end,
}
-- routes/posts/[year]/[month]/[slug].lua
return {
methods = { "GET" },
handler = function(ctx)
return Nixi.json({
year = ctx.params.year,
month = ctx.params.month,
slug = ctx.params.slug
})
end,
}
Layouts
Routes can specify a layout to wrap their content:
-- layouts/default.lua
return function(data)
local content = data.content or ""
return [[
<!DOCTYPE html>
<html lang="en">
<head>
<title>]] .. (data.title or "Nixi App") .. [[</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
]] .. content .. [[
</main>
</body>
</html>
]]
end
Using with app.lua
Routes are auto-loaded when the server starts:
-- app.lua
package.path = "src/?.lua;src/?/init.lua;" .. package.path
local Nixi = require("nixi.init")
local app = Nixi.new()
app.config = { host = "127.0.0.1", port = 3000 }
-- Routes from routes/ are auto-loaded
-- Additional routes can be defined here
app:get("/api/status", function(ctx)
return Nixi.json({ status = "ok" })
end)
return app