Skip to content

Server — Router and Decorators

Zolo's HTTP server runs in the host process (hyper + tokio) and does not terminate: calling http.serve(port, app) blocks the script and begins serving requests in a loop. To test it, open another terminal and use curl.

The first approach uses pipe composition: http.router() creates an empty router; each http.get("/route", handler) call attaches a route and returns the same router, ready for the next |>:

http.router() |> http.get(...) |> http.serve(port, app).

05-server-router.zolo
// Feature: HTTP server — router via pipes
// Syntax: `http.router() |> http.get("/path", handler) |> ...`
// When to use: simple APIs; declarative composition of routes with pipes.

use std::http

// Each handler receives `req` and returns a string, map, or `http.response(...)`.
fn handle_root(_req) {
  return "Hello from the Zolo server!"
}

fn handle_health(_req) {
  return #{status: "ok"}
}

let app = http.router()
  |> http.get("/", handle_root)
  |> http.get("/health", handle_health)

// Runs in the foreground on port 3000.
http.serve(3000, app)
// expected when running:
//   curl http://localhost:3000/        -> Hello from the Zolo server!
//   curl http://localhost:3000/health  -> {"status":"ok"}

Requires the Zolo CLI/host — open in the playground or run locally.

The second approach is the decorator style: annotating a fn with @get, @post, @put or @delete registers the route automatically; http.serve(port) without an app argument uses the global registry:

@get/@post/@put/@delete — automatic route registration.

06-server-decorators.zolo
// Feature: HTTP server — `@get`/`@post`/`@put`/`@delete` decorators
// Syntax: annotate a `fn` with the route; `http.serve(port)` registers them all.
// When to use: "framework" style, APIs with many routes, ergonomics
// close to Flask/Express, without wiring up a router manually.

use std::http

@get("/")
fn index() {
  return "Hello from decorator routes!"
}

@get("/health")
fn health() {
  return #{status: "ok"}
}

@post("/echo")
fn echo(req) {
  // `req.json()` reads the body as JSON and returns it as a map.
  return req.json()
}

@put("/items/:id")
fn update_item(req) {
  return #{updated: req.params.id}
}

@delete("/items/:id")
fn delete_item(req) {
  return #{deleted: req.params.id}
}

http.serve(3001)
// expected when running:
//   curl http://localhost:3001/                       -> Hello from decorator routes!
//   curl -X POST -d '{"x":1}' http://localhost:3001/echo
//   curl -X PUT  http://localhost:3001/items/42       -> {"updated":"42"}
//   curl -X DELETE http://localhost:3001/items/42     -> {"deleted":"42"}

Requires the Zolo CLI/host — open in the playground or run locally.

Each handler receives req and can return a string (200 text/plain), a map (200 application/json) or a value built with the response helpers shown later.

enespt-br