# Boko Framework

**Async web framework for Bux — inspired by FastAPI.**

Boko is a lightweight, multi-threaded web framework that brings FastAPI-style routing to the Bux programming language. It handles HTTP parsing, path pattern matching, query parameter extraction, and response building so you can focus on your application logic.

This version (0.2.0) is rewritten with modern Bux: `struct` methods, generic `StringMap<String>` for headers/query/path params, `for` loops, algebraic enums, `StringBuilder`, and string interpolation.

## Quick Start

```bash
cd apps/boko-framework
../../buxc build
./build/boko-framework
```

Open `http://localhost:8080` — you'll see the demo landing page.

## How It Works

Boko follows a **single-dispatch-function** pattern. You define one function — `Boko_Router` — and the framework calls it for every incoming request:

```bux
import Boko::{Request, Response, Response_Json, Response_Html, Response_NotFound,
              Path_Match,
              App, App_New, App_Run};

func Boko_Router(req: Request) -> Response {
    // Route: GET /
    if String_Eq(req.path, "/") {
        return Response_Html("<h1>Hello Boko!</h1>");
    }

    // Route: GET /api/health
    if String_Eq(req.path, "/api/health") {
        return Response_Json("{\"status\":\"ok\"}");
    }

    // Route: GET /users/{id} — path parameter
    if Path_Match("/users/{id}", req.path, &req) {
        let id: String = req.GetPathParam("id");
        // Build JSON response with id
        return Response_Json(...);
    }

    // Route: GET /search?q=... — query parameter
    if String_Eq(req.path, "/search") {
        let q: String = req.GetQuery("q");
        return Response_Json(...);
    }

    return Response_NotFound();
}

func Main() -> int {
    let app: App = App_New(8080, 4);  // port, threads
    App_Run(&app);
    return 0;
}
```

## API Reference

### Request

| Field | Type | Description |
|-------|------|-------------|
| `method` | `HttpVerb` | GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS |
| `path` | `String` | Request path (without query string) |
| `body` | `String` | Request body (for POST/PUT) |
| `headers` | `StringMap<String>` | Parsed request headers |
| `query` | `StringMap<String>` | Parsed query parameters |
| `pathParams` | `StringMap<String>` | Extracted path parameters |

| Method | Returns | Description |
|--------|---------|-------------|
| `req.GetHeader(name)` | `String` | Get header value by name |
| `req.GetQuery(name)` | `String` | Get query parameter by name |
| `req.HasQuery(name)` | `bool` | Check if query parameter exists |
| `req.GetPathParam(name)` | `String` | Get extracted path parameter |

### Response

| Constructor | Content-Type | Status |
|-------------|-------------|--------|
| `Response_Html(html)` | `text/html` | 200 |
| `Response_Json(json)` | `application/json` | 200 |
| `Response_Text(text)` | `text/plain` | 200 |
| `Response_Redirect(url)` | — | 302 |
| `Response_NotFound()` | `application/json` | 404 |
| `Response_Error(code, msg)` | `application/json` | custom |
| `Response_NoContent()` | — | 204 |

### Path Matching

`Path_Match(pattern, actualPath, req)` matches a pattern with `{param}` placeholders:

```bux
// Pattern:    /users/{id}/posts/{postId}
// Actual:     /users/42/posts/7
// Extracts:   id=42, postId=7

if Path_Match("/users/{id}/posts/{postId}", req.path, &req) {
    let userId: String = req.GetPathParam("id");      // "42"
    let postId: String = req.GetPathParam("postId");  // "7"
}
```

The function returns `true` if the pattern matches and populates `req.pathParams`.

### App

```bux
let app: App = App_New(8080, 4);  // port 8080, 4 worker threads
App_Run(&app);                     // blocks, handles requests
```

## Architecture

```
Incoming connection
    │
    ▼
Net_Accept()  ───  worker thread (1 of N)
    │
    ▼
Net_Recv()  →  raw HTTP bytes
    │
    ▼
Request_Parse()  →  method, path, query, headers, body
    │
    ▼
Boko_Router(req)  ←── YOUR CODE
    │
    ▼
Response_Build()  →  HTTP/1.1 response string
    │
    ▼
Net_Send()  →  bytes to client
    │
    ▼
Net_Close()
```

## Endpoints in Demo App

| Method | Path | Description |
|--------|------|-------------|
| GET | `/` | Landing page (HTML) |
| GET | `/api/health` | Health check (JSON) |
| GET | `/api/info` | Framework info (JSON) |
| GET | `/hello?name=X` | Query param demo |
| GET | `/users/{id}` | Path param demo |
| GET | `/posts/{id}/comments/{cid}` | Multi path param demo |
| GET | `/redirect` | 302 redirect to `/` |
| POST | `/api/echo` | Echo request body |

## Project Structure

```
apps/boko-framework/
├── bux.toml              # Package manifest
├── README.md             # This file
└── src/
    ├── Boko.bux          # Framework core (~500 lines)
    └── Main.bux          # Example app (~150 lines)
```

## Design Philosophy

Boko is intentionally simple. Instead of complex DSLs or code generation, it gives you:

- **One function to write** — `Boko_Router(req) -> Response`
- **Direct control** — you write plain if/else or match for routing
- **No magic** — path params, query params, headers are explicit function calls
- **Fast** — multi-threaded accept loop, zero allocations where possible

This is the same philosophy as Go's `net/http` — simple, explicit, composable.

## License

Part of the Bux project. See root [LICENSE](../../LICENSE).
