Fist 👊
A declarative, type-safe router for the Mist web server in Gleam, inspired by Axum.
Features
- Declarative API: Build your router with a clean, chainable syntax:
fist.get("/", to: handler). - Dynamic Routing: Capture URL parameters with
:parameter_name. - Generic Output: Handlers can return anything (
Response,String, or your own custom types). - Transformation Pipeline: Use
fist.mapto transform your router’s output (e.g., fromStringtomist.ResponseData). - Response Helpers: Built-in functions like
fist.ok,fist.json, andfist.textfor faster development. - Type Safe: Gleam’s type system ensures your handlers always match your router’s expectations.
Installation
Add fist to your gleam.toml:
[dependencies]
fist = { path = "../fist" } # Or from Hex when available
Quick Start
1. Define your handlers
With fist, you can return standard Response types using built-in helpers.
import gleam/dict.{type Dict}
import gleam/http/request.{type Request}
import gleam/result
import fist
fn hello_handler(_req: Request(body), params: Dict(String, String)) {
let name = dict.get(params, "name") |> result.unwrap("stranger")
fist.ok("Hello, " <> name <> "!")
}
2. Create and transform the router
You can write your business logic using simple Response(String) and then transform it for your web server (like Mist) at the end.
import fist
pub fn main() {
fist.new()
|> fist.get("/hello/:name", to: hello_handler)
|> fist.get("/json", to: fn(_, _) { fist.json("{\"status\": \"ok\"}") })
// Transform all Response(String) to Response(mist.ResponseData)
|> fist.map(fist.render_mist)
|> fist.start(port: 8080)
}
Advanced: Custom Return Types (ADTs)
Because fist is generic over the handler’s output, you can use your own Algebraic Data Types and map them.
pub type MyAnswer {
Success(String)
UserFound(User)
Error(String)
}
fn my_handler(_, _) {
Success("Operation completed")
}
pub fn start() {
fist.new()
|> fist.get("/", to: my_handler)
|> fist.map(fn(answer) {
case answer {
Success(msg) -> fist.ok(msg)
UserFound(user) -> fist.json(user_to_json(user))
Error(err) -> fist.text("Error: " <> err) // Adicione .set_status(400) se necessário
}
})
|> fist.map(fist.render_mist)
|> fist.start(8080)
}
Integration with Mist
The fist.start function provides a convenient way to run your router with Mist. It expects a router where handlers return Response(mist.ResponseData).
import mist
import fist
pub fn main() {
fist.new()
|> fist.get("/", to: fn(_req, _params) {
fist.ok("Hello from Mist!")
})
|> fist.map(fist.render_mist)
|> fist.start(port: 8080)
}