Skip to content

Chapter 17: Web Development

Build web applications entirely in Wyn - HTTP server, routing, templates, database, and deployment.

Quick Start

bash
wyn init myapp --web
cd myapp
wyn run
# Server running at http://localhost:8080

HTTP Server

wyn
fn handle(method: string, path: string, body: string, fd: int) {
    if path == "/" {
        Http.respond(fd, 200, "<h1>Hello!</h1>")
        return
    }
    if path == "/api/data" {
        Http.respond_json(fd, 200, "{\"status\": \"ok\"}")
        return
    }
    Http.respond(fd, 404, "Not Found")
}

fn main() -> int {
    var server = Http.listen(8080)
    while true {
        var req = Http.accept(server)
        if req > 0 {
            spawn handle_request(req)
        }
    }
    return 0
}

fn handle_request(req: int) -> int {
    handle(Http.method(req), Http.path(req), Http.body(req), req)
    Http.close_client(req)
    System.gc()
    return 0
}

Each request is handled in its own spawned task - concurrent by default.

Templates

Create templates/page.html:

html
<h1>${title}</h1>
${if:logged_in}
<p>Welcome, ${username}!</p>
<ul>
${each:items}<li>${item}</li>
${endeach}</ul>
${else}
<p>Please log in</p>
${endif}

Render from Wyn:

wyn
var ctx = { "title": "My App", "logged_in": "true", "username": "Alice" }
ctx.insert("items", "Home,About,Contact")

var html = Template.render("templates/page.html", ctx)
Http.respond(fd, 200, html)

Values are HTML-escaped by default. Use ${raw:key} for trusted HTML.

Database

wyn
var db = Db.open("app.db")
Db.exec(db, "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")

// Always use parameterized queries - prevents SQL injection
Db.exec_p(db, "INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "[email protected]"])
var users = Db.query_p(db, "SELECT name FROM users WHERE name LIKE ?", ["%ali%"])

Environment Config

wyn
System.load_env(".env")
var port = System.env("PORT")
var db_path = System.env("DATABASE_URL")

Deployment

Add to wyn.toml:

toml
[deploy.prod]
host = "myserver.com"
user = "deploy"
key = "~/.ssh/id_ed25519"
path = "/opt/myapp"
os = "linux"
pre = "systemctl stop myapp"
post = "systemctl start myapp"

Deploy:

bash
wyn deploy prod

One command: cross-compiles for Linux, uploads binary, restarts service. Your entire app is a single binary with no dependencies.

Exercises

  1. Build a URL shortener with SQLite storage
  2. Add a JSON API that returns a list of items
  3. Create a template-based blog with multiple pages

Previous: Chapter 16: Python Libraries | Next: Chapter 18: Toolchain

MIT License - v1.19.1