HTTP servers
A server declaration binds an address, routes, and effect requirements into a declarative HTTP server.
Declaration
cleat
import "std/server" import "std/json" fn health(req: server.Request) -> Result[server.Response, string] { return Ok(server.json_response(200, json.encode(HealthStatus { status: "ok" }))) } fn get_user(req: server.Request) -> Result[server.Response, string] needs { net } { let id = server.get_param(req.params, "id") return Ok(server.json_response(200, json.encode(User { id: id }))) } server API { addr: ":8080" needs { net } get "/health" = health get "/users/:id" = get_user post "/api/review" = handle_review }
Fields
| Field | Required | Description |
|---|---|---|
addr: | yes | Listen address (e.g., ":8080", "localhost:3000") |
needs | yes | Must include net |
| Routes | yes | At least one route required |
Route syntax
text
method "path" = handler_function
Methods: get, post, put, delete, patch
Path parameters
Segments starting with : are extracted as named parameters:
cleat
get "/users/:id" = get_user get "/users/:id/posts/:post_id" = get_post
Access via server.get_param(req.params, "id").
Starting the server
cleat
fn main() -> int needs { net } { return match API.start() { Ok(_) => 0, Err(e) => { io.println("error: ${e}") 1 }, } }
Server.start() blocks until the process receives SIGINT or SIGTERM, then performs graceful shutdown.
Handler signature
cleat
fn handler(req: server.Request) -> Result[server.Response, string] needs { ... } { // ... }
Handlers that return Err(msg) automatically produce a 500 JSON response.
Request fields
| Field | Type | Description |
|---|---|---|
method | string | HTTP method |
path | string | URL path |
body | string | Request body |
query | string | Raw query string |
headers | []server.Header | Request headers |
params | []server.Param | Path parameters |
request_id | string | Correlation ID |
Response helpers
| Function | Description |
|---|---|
server.text(status, body) | Plain text response |
server.json_response(status, body) | JSON response with Content-Type header |
server.error_response(status, message) | JSON error response |
server.redirect(status, location) | Redirect response |
Correlation IDs
The server automatically extracts X-Request-ID from incoming requests. If absent, it generates a UUID. This ID:
- Prefixes all log output:
[INFO] [abc-123] handling request - Flows to all outbound HTTP requests via
X-Request-IDheader - Is available in handlers via
req.request_id - Appears in supervisor trace entries
- Each request gets isolated effects — concurrent requests don't interfere
Compiler enforcement
- Server must declare
needs { net } - At least one route required
- No duplicate routes (same method + path)
- Handler functions must exist
- Handler effects must be covered by the server's
needs