Skip to content

std/http

HTTP server with Hono-inspired router, context, middleware, path params, query strings, and cookies.

milo
from "std/http" import { Context, Response, Router, serveRouter }

Types

Request

milo
struct Request {
    method: string,
    path: string,
    queryParams: Vec<Param>,
    headers: Vec<Param>,
    body: string,
}

Param

milo
struct Param {
    name: string,
    value: string,
}

Used for path params, query params, headers, and cookies.

Context

milo
struct Context {
    req: Request,
    params: Vec<Param>,
    statusCode: i32,
    respHeaders: Vec<Param>,
}

Context is passed to route handlers and provides access to request data and response building.

Context Methods

MethodDescription
ctx.param("name")Extract path parameter (:name in pattern)
ctx.query("key")Extract query string value (?key=value)
ctx.header("name")Read request header (case-insensitive)
ctx.cookie("name")Read cookie value from request
ctx.req.bodyAccess raw request body
ctx.setStatus(code)Set response status code
ctx.setHeader(name, value)Add response header
ctx.setCookie(name, value)Set response cookie
ctx.setCookieWithOptions(name, value, opts)Set cookie with options ("Path=/; HttpOnly")
ctx.deleteCookie(name)Delete cookie (Max-Age=0)
ctx.text(body)Return text/plain response
ctx.json(body)Return application/json response
ctx.html(body)Return text/html response
ctx.redirect(url)Return 302 redirect

Response

milo
enum Response {
    Text(string),
    Html(string),
    Json(string),
    NotFound,
    Status(i32, string, string),
}

Router

milo
struct Router {
    routes: Vec<Route>,
    middleware: Vec<...>,
}

Router Methods

MethodDescription
Router.new()Create a new router
r.get(pattern, handler)Register GET route
r.post(pattern, handler)Register POST route
r.put(pattern, handler)Register PUT route
r.delete(pattern, handler)Register DELETE route
r.all(pattern, handler)Register route for any method
r.use(middleware)Add middleware

Functions

serve

milo
fn serve(port: u16?, handler: (&Request) => Response): Result<void>

Simple server — takes a port and handler function. Good for static file servers or single-handler apps.

serveRouter

milo
fn serveRouter(port: u16?, router: &Router): Result<void>

Start an HTTP server using a Router. Context respHeaders are sent on the wire.

Examples

Router with path params

milo
from "std/http" import { Context, Response, Router, serveRouter }

fn userHandler(ctx: &mut Context): Response {
    let id = ctx.param("id")
    return ctx.json($"\{\"userId\": \"{id}\"}")
}

fn searchHandler(ctx: &mut Context): Response {
    let q = ctx.query("q")
    return ctx.text($"searching for: {q}")
}

fn main(): i32 {
    var r: Router = Router.new()
    r.get("/users/:id", userHandler)
    r.get("/search", searchHandler)
    serveRouter(8080, r)
    return 0
}

Middleware

milo
fn logMiddleware(ctx: &mut Context, next: (&mut Context) => Response): Response {
    print(ctx.req.method + " " + ctx.req.path)
    let resp = next(ctx)
    ctx.setHeader("X-Powered-By", "milo")
    return resp
}

fn main(): i32 {
    var r: Router = Router.new()
    r.use(logMiddleware)
    r.get("/", fn homeHandler(ctx: &mut Context): Response {
        return ctx.text("hello")
    })
    serveRouter(3000, r)
    return 0
}

Cookies

milo
fn loginHandler(ctx: &mut Context): Response {
    ctx.setCookieWithOptions("session", "abc123", "Path=/; HttpOnly")
    return ctx.json("{\"logged_in\": true}")
}

fn profileHandler(ctx: &mut Context): Response {
    let session = ctx.cookie("session")
    if session.len == 0 {
        ctx.setStatus(401)
        return ctx.json("{\"error\": \"not authenticated\"}")
    }
    return ctx.json($"\{\"session\": \"{session}\"}")
}