HTTP Server
Zolo includes a built-in HTTP server module for building web applications and APIs. Import it with use std::http.
Quick Start #
use std::http
fn handle_root(_req) {
return "Hello from Zolo!"
}
let app = http.router()
|> http.get("/", handle_root)
http.serve(3000, app)
This starts an HTTP server on port 3000 that responds with "Hello from Zolo!" on the root path.
Router and Routes #
Create a router with http.router() and register route handlers using http.get() and http.post(). Routes are chained with the pipe operator |>:
let app = http.router()
|> http.get("/health", health_handler)
|> http.get("/users/:id", get_user)
|> http.post("/echo", echo_handler)
| Function | Description |
|---|---|
http.router() |
Create a new router |
http.get(router, path, handler) |
Register a GET route |
http.post(router, path, handler) |
Register a POST route |
Path Parameters #
Define dynamic segments in routes with :param syntax. Access them via req.params:
fn handle_user(req) {
let id = req.params.id
return #{"id": id, "name": "User"}
}
fn handle_greet(req) {
let name = req.params.name
return #{"greeting": "Hello, {name}!"}
}
let app = http.router()
|> http.get("/users/:id", handle_user)
|> http.get("/api/greet/:name", handle_greet)
A request to /users/42 sets req.params.id to "42".
Query Parameters #
Access query string parameters via req.query. Use ?? to provide defaults:
fn handle_greet(req) {
let name = req.query.name ?? "World"
return "Hello, {name}!"
}
A request to /greet?name=Alice sets req.query.name to "Alice".
Request Object #
The handler receives a request object with the following fields:
| Field | Type | Description |
|---|---|---|
req.method |
str |
HTTP method ("GET", "POST", etc.) |
req.path |
str |
Request path (e.g. "/users/42") |
req.params |
table |
Path parameters (e.g. req.params.id) |
req.query |
table |
Query string parameters (e.g. req.query.name) |
req.headers |
table |
Request headers |
req.body |
str |
Raw request body |
req.json() |
fn |
Parse body as JSON and return a table |
Reading JSON Body #
Use req.json() to parse incoming JSON payloads:
fn handle_echo(req) {
return req.json()
}
With the pipe operator:
fn handle_echo(req) {
return req |> .json()
}
Response Types #
Handlers return a value that determines the response format:
| Return Value | Content-Type | Behavior |
|---|---|---|
| String | text/plain |
Returned as plain text with status 200 |
Table (#{}) |
application/json |
Automatically serialized to JSON with status 200 |
http.html(body) |
text/html |
Returned as HTML with status 200 |
http.redirect(url) |
— | Sends a redirect response to the given URL |
http.response(status, body) |
text/plain |
Returned with a custom status code |
Examples #
// Plain text
fn text_handler(_req) {
return "Hello from Zolo!"
}
// JSON (return a table)
fn json_handler(_req) {
return #{"message": "Hello", "language": "Zolo", "version": "0.1.0"}
}
// HTML
fn html_handler(_req) {
return http.html("<h1>Hello from Zolo!</h1>")
}
// Redirect
fn redirect_handler(_req) {
return http.redirect("/")
}
// Custom status code
fn created_handler(_req) {
return http.response(201, "Created!")
}
Adding Headers to a Response #
Use .with_headers() to attach custom headers to a response:
fn handler(req) {
return #{"data": "value"}.with_headers(#{
"X-Custom-Header": "custom-value"
})
}
Middleware #
Middleware functions wrap route handlers to add cross-cutting behavior such as logging, authentication, or CORS headers. A middleware receives the request and a next function:
fn logger(req, next) {
print("[{req.method}] {req.path}")
return next(req)
}
Register middleware with http.middleware():
let app = http.router()
|> http.get("/api/health", health_handler)
|> http.middleware(logger)
Middleware can also modify the response by chaining .with_headers() on the result of next:
fn cors(req, next) {
return next(req).with_headers(#{
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
"Access-Control-Allow-Headers": "Content-Type"
})
}
Multiple middleware functions can be stacked:
let app = http.router()
|> http.get("/api/health", health_handler)
|> http.middleware(logger)
|> http.middleware(cors)
Workers Configuration #
Use http.workers(n) to set the number of worker threads for the server:
http.workers(1)
http.serve(3000, app)
Complete Example #
use std::http
// --- Handlers ---
fn handle_root(_req) {
return "Hello from Zolo!"
}
fn handle_json(_req) {
return #{"message": "Hello", "language": "Zolo", "version": "0.1.0"}
}
fn handle_user(req) {
let id = req.params.id
return #{"id": id, "name": "User"}
}
fn handle_echo(req) {
return req.json()
}
fn handle_greet(req) {
let name = req.query.name ?? "World"
return "Hello, {name}!"
}
fn handle_html(_req) {
return http.html("<h1>Hello from Zolo!</h1>")
}
fn handle_redirect(_req) {
return http.redirect("/")
}
fn handle_status(_req) {
return http.response(201, "Created!")
}
// --- Middleware ---
fn logger(req, next) {
print("[{req.method}] {req.path}")
return next(req)
}
fn cors(req, next) {
return next(req).with_headers(#{
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE",
"Access-Control-Allow-Headers": "Content-Type"
})
}
// --- Router ---
let app = http.router()
|> http.get("/", handle_root)
|> http.get("/json", handle_json)
|> http.get("/users/:id", handle_user)
|> http.post("/echo", handle_echo)
|> http.get("/greet", handle_greet)
|> http.get("/html", handle_html)
|> http.get("/redirect", handle_redirect)
|> http.get("/status", handle_status)
|> http.middleware(logger)
|> http.middleware(cors)
// --- Start server ---
http.workers(1)
http.serve(3000, app)
API Reference #
| Function | Description |
|---|---|
http.router() |
Create a new router |
http.get(router, path, handler) |
Register a GET route |
http.post(router, path, handler) |
Register a POST route |
http.middleware(router, fn) |
Add middleware to the router |
http.serve(port, router) |
Start the server on the given port |
http.workers(n) |
Set the number of worker threads |
http.html(body) |
Create an HTML response |
http.redirect(url) |
Create a redirect response |
http.response(status, body) |
Create a response with a custom status code |