# HTTP Server Deft's HTTP server is built on top of the TCP primitives and runs on the same epoll event loop. Each request is handled by a coroutine, so handlers can `await` async operations (database queries, upstream HTTP calls, timers) without blocking other connections. Routers are built from standard maps wrapped with `http/router`:
 def app %{...} |> [http/router] 
## Hello, Server
 def app %{
    routes @{
        %{ path "/" method :GET handler { "Hello, World!" } }
    }
} |> [http/router]

http/serve $app :8080 
Run with `deft server.dft`, then `curl http://localhost:8080/`. ## Router Construction A router is a map with these optional keys:
 def app %{
    prefix "/api"                                  # optional path prefix
    routes @{
        %{ path "/"            method :GET  handler root-handler }
        %{ path "/users/:id"   method :GET  handler get-user }
        %{ path "/users"       method :POST handler create-user }
        %{ path "/files/*rest" method :GET  handler get-file }
    }
    statics @{
        %{ path "/assets" dir "./public" }
    }
    middleware @{
        {req res} { echo "request: $req~method $req~path" }
    }
} |> [http/router] 
### `routes` A list of route maps. Each route has: - `path` — URL pattern. `:name` captures a path segment into `$req~params~name`. `*name` captures the rest of the path (zero or more segments). - `method` — `:GET`, `:POST`, `:PUT`, `:PATCH`, `:DELETE`, `:HEAD`. - `handler` — a function `{req res}` or a closure. ### `statics` A list of static-file directories:
 %{ path "/assets" dir "./public" } 
Requests under `/assets/` are served from files under `./public/`. Use `--watch` on `http/serve` to log filesystem changes during development. ### `prefix` A path prefix applied to every route and static in the router:
 def api %{
    prefix "/api/v1"
    routes @{
        %{ path "/users" method :GET handler list-users }    # GET /api/v1/users
    }
} |> [http/router] 
### `middleware` A list of handler fns invoked in order before the matched route. Middleware receives `{req res}` and can short-circuit by writing a response. ## Request and Response Handlers receive `%Request{}` and `%Response{}` tagged maps.
 def get-user {|req res|
    @doc "Fetch a user by id."
    set id $req~params~id                    # captured from :id in the path
    set user [load-user-from-db $id]
    if $user {
        http/json $res $user
    } else {
        http/status $res 404
        http/html $res "not found"
    }
} 
### Request fields | Field | Type | Notes | |---|---|---| | `:method` | keyword | `:GET`, `:POST`, etc. | | `:path` | string | URL path | | `:headers` | map | Lowercased header names → values | | `:params` | map | Path captures (`:id` → `$req~params~id`) | | `:query` | map | Query string parsed | | `:body` | string | Request body | ### Response helpers | Function | Purpose | |---|---| | `[http/respond $res $body $content-type]` | Write response body | | `[http/json $res $data]` | JSON response (auto-encodes) | | `[http/html $res $html]` | HTML response | | `[http/status $res $code]` | Set status code | | `[http/header $res "name" "value"]` | Set response header | | `[http/redirect $res "/url" ?code]` | Redirect (default 302) | ## Implicit vs Explicit Response Two ways to send a response: **Implicit** — return the body from the handler:
 %{ path "/" method :GET handler { "Hello, World!" } } 
A bare string return becomes a 200 OK with the string as the body. **Explicit** — write via response helpers:
 def get-user {|req res|
    http/status $res 200
    http/header $res "Content-Type" "application/json"
    http/respond $res [json/stringify $user] "application/json"
} 
Use explicit when you need to set status, headers, or a specific content type. ## Async Handlers Handlers run as coroutines on the event loop. Each request spawns a coroutine; the connection stays alive until the coroutine completes. Use `await` for any async operation:
 def proxy {|req res|
    @doc "Proxy to an upstream service."
    set backend [await [http/get [str "http://127.0.0.1:3001" $req~path]]]
    http/status $res $backend~status
    http/respond $res $backend~body "application/json"
}

def api %{
    routes @{ %{ path "/api/*rest" method :GET handler $proxy } }
} |> [http/router] 
 def slow {|req res|
    @doc "Sleep 2 seconds, non-blocking for other connections."
    sleep 2000
    http/html $res "<h1>done</h1>"
} 
Synchronous handlers continue to work identically — they run to completion in a single event loop tick. ## Keep-Alive By default connections close after each response. To enable HTTP/1.1 keep-alive:
 http/header $res "Connection" "keep-alive" 
## HTML Templating `[html ... \html]` blocks produce an HTML string. The full templating system — interpolation forms, escaping, partials, defining your own template blocks, and `[sql ... \sql]` — is documented in [Templating](templating).
 def render-user {|user|
    [html
        <div class="user">
            <h1>$user~name</h1>
            <p>Age: $user~age</p>
        </div>
    \html]
}

def handler {|req res|
    http/html $res [render-user $alice]
} 
## Static Files
 def app %{
    statics @{
        %{ path "/assets"   dir "./public" }
        %{ path "/downloads" dir "/var/files" }
    }
    routes @{
        %{ path "/" method :GET handler { [fs/read "./public/index.html"] } }
    }
} |> [http/router] 
Add `--watch` to log changes:
 http/serve $app :8080 --watch 
## Middleware Middleware fires in order before the matched route. Each middleware fn receives `{req res}`:
 def logger {|req res|
    @doc "Log every request."
    echo "$req~method $req~path"
}

def auth {|req res|
    @doc "Require an Authorization header."
    if ![get $req~headers "authorization"] {
        http/status $res 401
        http/html $res "auth required"
        return
    }
}

def api %{
    middleware @{ $logger $auth }
    routes @{ %{ path "/secure" method :GET handler { "secret" } } }
} |> [http/router] 
### Async middleware Middleware runs as coroutines — the same machinery as route handlers — so `[await ...]` works inside them. A middleware that awaits a database lookup suspends only its own request; other connections keep running:
 def load-user {|req res|
    @doc "Resolve the Authorization token to a user before the handler runs."
    def token [get $req~headers "authorization"]
    if token {
        set user [await [sqlite/query $db "SELECT * FROM users WHERE token = ?" @{ $token }]]
        if ![empty? $user] {
            [http/header $res "X-User" $user~first~name]
        }
    }
}

def api %{
    middleware @{ $load-user }
    routes @{ %{ path "/me" method :GET handler { "profile" } } }
} |> [http/router] 
A middleware that returns `false` rejects the request — the response state is flushed as-is and neither downstream middleware nor the route handler runs. A middleware that returns early (after writing a response) has the same effect: it prevents downstream middleware and the route handler from running. ## Multi-Dispatch Pattern For non-trivial handlers, define the logic separately from the HTTP adapter:
 # Pure logic — testable without HTTP
def load-user {|id^string|
    [db-query "SELECT * FROM users WHERE id = ?" @{ $id }]
}

# HTTP adapter
def get-user {|req res|
    set user [load-user $req~params~id]
    http/json $res $user
}

def api %{
    routes @{ %{ path "/users/:id" method :GET handler $get-user } }
} |> [http/router] 
This pattern (logic in `def`, HTTP shape in handler) keeps handlers small and lets you unit-test the business logic without spinning up a server. ## Examples ### JSON API
 def api %{
    prefix "/api/v1"
    routes @{
        %{ path "/users"      method :GET  handler list-users }
        %{ path "/users/:id"  method :GET  handler get-user }
        %{ path "/users"      method :POST handler create-user }
    }
} |> [http/router]

def users-db @{}

def list-users {|req res| http/json $res $users-db }

def get-user {|req res|
    set id $req~params~id
    set u [get $users-db $id]
    if $u {
        http/json $res $u
    } else {
        http/status $res 404
        http/json $res %Err{ :message "not found" }
    }
}

def create-user {|req res|
    set data [json/parse $req~body]
    set id $data~id
    set users-db [assoc $users-db $id $data]
    http/status $res 201
    http/json $res $data
}

http/serve $api :8080 
### SPA with static + fallback
 def app %{
    statics @{
        %{ path "/assets" dir "./dist/assets" }
    }
    routes @{
        %{ path "/*rest" method :GET handler { [fs/read "./dist/index.html"] } }
    }
} |> [http/router] 
## Server Lifecycle
 def srv [http/serve $app :8080]

# ... later ...

http/stop $srv 
`http/stop` is graceful — in-flight requests finish before the server exits. ## Cluster Mode For production workloads, the App Server runs the same app in N worker processes, each binding the same port with `SO_REUSEPORT` — the kernel load-balances accepts with no application-level proxy hop. This is the App Server; see [App Server](app-server) for cluster deployment, reloads, and cross-worker state. ## Quick Reference | Form | Purpose | |---|---| | `def name %{ ... } \|> [http/router]` | Build a router | | `[http/serve $router :port]` | Start server | | `[http/serve $router :port --watch]` | Watch static dirs | | `[http/stop $server]` | Stop server | | `[http/respond $res $body $content-type]` | Write response | | `[http/json $res $data]` | JSON response | | `[http/html $res $html]` | HTML response | | `[http/status $res $code]` | Set status | | `[http/header $res "name" "value"]` | Set header | | `[http/redirect $res "/url" ?code]` | Redirect | | Cluster stdlib functions | See [App Server](app-server) |