// HTTP API Example // // A complete REST API demonstrating: // - Route matching with path parameters // - Response builders // - JSON construction // // Run with: lux examples/http_api.lux // Test with: // curl http://localhost:8080/ // curl http://localhost:8080/users // curl http://localhost:8080/users/42 // ============================================================ // Response Helpers // ============================================================ fn httpOk(body: String): { status: Int, body: String } = { status: 200, body: body } fn httpCreated(body: String): { status: Int, body: String } = { status: 201, body: body } fn httpNotFound(body: String): { status: Int, body: String } = { status: 404, body: body } fn httpBadRequest(body: String): { status: Int, body: String } = { status: 400, body: body } // ============================================================ // JSON Helpers // ============================================================ fn jsonEscape(s: String): String = String.replace(String.replace(s, "\\", "\\\\"), "\"", "\\\"") fn jsonStr(key: String, value: String): String = "\"" + jsonEscape(key) + "\":\"" + jsonEscape(value) + "\"" fn jsonNum(key: String, value: Int): String = "\"" + jsonEscape(key) + "\":" + toString(value) fn jsonObj(content: String): String = "{" + content + "}" fn jsonArr(content: String): String = "[" + content + "]" fn jsonError(message: String): String = jsonObj(jsonStr("error", message)) // ============================================================ // Path Matching // ============================================================ fn pathMatches(path: String, pattern: String): Bool = { let pathParts = String.split(path, "/") let patternParts = String.split(pattern, "/") if List.length(pathParts) != List.length(patternParts) then false else matchParts(pathParts, patternParts) } fn matchParts(pathParts: List, patternParts: List): Bool = { if List.length(pathParts) == 0 then true else { match List.head(pathParts) { None => true, Some(pathPart) => { match List.head(patternParts) { None => true, Some(patternPart) => { let isMatch = if String.startsWith(patternPart, ":") then true else pathPart == patternPart if isMatch then { let restPath = Option.getOrElse(List.tail(pathParts), []) let restPattern = Option.getOrElse(List.tail(patternParts), []) matchParts(restPath, restPattern) } else false } } } } } } fn getPathSegment(path: String, index: Int): Option = { let parts = String.split(path, "/") List.get(parts, index + 1) } // ============================================================ // Handlers // ============================================================ fn indexHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("message", "Welcome to Lux HTTP API"))) fn healthHandler(): { status: Int, body: String } = httpOk(jsonObj(jsonStr("status", "healthy"))) fn listUsersHandler(): { status: Int, body: String } = { let user1 = jsonObj(jsonNum("id", 1) + "," + jsonStr("name", "Alice")) let user2 = jsonObj(jsonNum("id", 2) + "," + jsonStr("name", "Bob")) httpOk(jsonArr(user1 + "," + user2)) } fn getUserHandler(path: String): { status: Int, body: String } = { match getPathSegment(path, 1) { Some(id) => { let body = jsonObj(jsonStr("id", id) + "," + jsonStr("name", "User " + id)) httpOk(body) }, None => httpNotFound(jsonError("User not found")) } } fn createUserHandler(body: String): { status: Int, body: String } = { let newUser = jsonObj(jsonNum("id", 3) + "," + jsonStr("name", "New User")) httpCreated(newUser) } // ============================================================ // Router // ============================================================ fn router(method: String, path: String, body: String): { status: Int, body: String } = { if method == "GET" && path == "/" then indexHandler() else if method == "GET" && path == "/health" then healthHandler() else if method == "GET" && path == "/users" then listUsersHandler() else if method == "GET" && pathMatches(path, "/users/:id") then getUserHandler(path) else if method == "POST" && path == "/users" then createUserHandler(body) else httpNotFound(jsonError("Not found: " + path)) } // ============================================================ // Server // ============================================================ fn serveLoop(remaining: Int): Unit with {Console, HttpServer} = { if remaining <= 0 then { Console.print("Max requests reached, stopping server.") HttpServer.stop() } else { let req = HttpServer.accept() Console.print(req.method + " " + req.path) let resp = router(req.method, req.path, req.body) HttpServer.respond(resp.status, resp.body) serveLoop(remaining - 1) } } fn main(): Unit with {Console, HttpServer} = { let port = 8080 let maxRequests = 10 Console.print("========================================") Console.print(" Lux HTTP API Demo") Console.print("========================================") Console.print("") Console.print("Endpoints:") Console.print(" GET / - API info") Console.print(" GET /health - Health check") Console.print(" GET /users - List users") Console.print(" GET /users/:id - Get user by ID") Console.print(" POST /users - Create user") Console.print("") Console.print("Try:") Console.print(" curl http://localhost:8080/") Console.print(" curl http://localhost:8080/users") Console.print(" curl http://localhost:8080/users/42") Console.print(" curl -X POST http://localhost:8080/users") Console.print("") Console.print("Starting server on port " + toString(port) + "...") HttpServer.listen(port) Console.print("Server listening!") serveLoop(maxRequests) } let output = run main() with {}