# Lux Web Framework A full-stack web framework for building websites with Lux. Provides routing, layouts, a blog engine, form processing, SEO helpers, static file serving, and middleware — all built on Lux's effect system. ## Quick Start ```lux import web fn main(): Unit with {Console, HttpServer, File} = { let app = web.app("My Site", "A Lux website", "./static", "./content") let app = web.addRoute(app, web.get("/", homePage)) let app = web.addRoute(app, web.get("/about", aboutPage)) web.serve(app, 8080) } fn homePage(req: web.Request): web.Response = web.htmlResponse(200, web.layout(myLayout, "Home | My Site", "Welcome", web.hero("Welcome", "Build something great", "/about", "Learn More") ) ) ``` ## Features | Feature | Description | |---------|-------------| | **Effects-based** | Every handler declares its side effects (`File`, `Sql`, `HttpServer`) | | **Type-safe** | Elm-style Html builder catches broken markup at compile time | | **Zero JS** | Server-rendered HTML. No client bundle. Pages work without JavaScript | | **Blog engine** | Markdown + frontmatter blog with zero config | | **SQLite built-in** | `Sql` effect is a language primitive. No ORM setup | | **Single binary** | Compile to C via `lux compile` for single binary deployment | ## API Reference ### `web.app` — Application Builder ```lux web.app(title, description, staticDir, contentDir): App web.addRoute(app, route): App web.addMiddleware(app, middleware): App web.serve(app, port): Unit with {Console, HttpServer, File} ``` ### Route Builders ```lux web.get(pattern, handler): Route web.post(pattern, handler): Route web.put(pattern, handler): Route web.delete(pattern, handler): Route ``` ### `web.page` — Layout & Components ```lux web.layout(config, title, description, body): String web.hero(title, subtitle, ctaUrl, ctaText): String web.miniHero(title, subtitle): String web.card(icon, title, description): String web.cardGrid(cards): String web.statsBar(stats): String web.emailSignup(heading, buttonText, action): String web.ctaSection(heading, ctaUrl, ctaText): String ``` ### `web.blog` — Blog Engine ```lux web.loadPosts(contentDir, section): List with {File} web.latestPosts(contentDir, section, count): List with {File} web.loadPost(contentDir, section, slug): Option with {File} web.blogIndexHtml(posts): String web.blogPostHtml(post): String web.rssFeed(title, url, description, posts): String ``` ### `web.form` — Form Processing ```lux web.parseBody(body): List<(String, String)> web.getField(fields, name): Option web.requireField(fields, name): Result web.validateEmail(email): Result web.sanitize(input): String ``` ### `web.db` — Database Helpers ```lux web.saveSubscriber(dbPath, email): Result with {Sql} web.getSubscribers(dbPath): List with {Sql} ``` ### `web.seo` — SEO Helpers ```lux web.metaTags(config): String web.structuredData(name, description, url): String web.sitemap(pages): String web.robots(sitemapUrl): String ``` ### `web.static` — Static File Serving ```lux web.serveStatic(basePath, requestPath): Response with {File} web.mimeType(path): String web.cacheHeaders(maxAge): List<(String, String)> ``` ### `web.middleware` — Composable Middleware ```lux web.logging(): Middleware with {Console} web.cors(origin): Middleware web.securityHeaders(): Middleware ``` ### Response Helpers ```lux web.htmlResponse(status, body): Response web.jsonResponse(status, body): Response web.textResponse(status, body): Response web.redirect(location): Response web.redirectPermanent(location): Response ``` ## Guide: Build a Marketing Site 1. Create project structure: ``` my-site/ ├── lux.toml ├── main.lux ├── content/blog/ ├── static/styles.css ``` 2. Add dependency in `lux.toml`: ```toml [dependencies] web = { version = "0.1.0", path = "../lux/packages/web" } ``` 3. Define your routes and pages in `main.lux` 4. Add blog posts as markdown files in `content/blog/` 5. Run: `lux main.lux` ## Guide: Add a Blog ```lux // Load and display blog posts let app = web.addRoute(app, web.get("/blog", fn(req: web.Request): web.Response with {File} => { let posts = web.loadPosts("./content", "blog") web.htmlResponse(200, web.layout(myLayout, "Blog", "Our blog", web.miniHero("Blog", "Latest posts") + web.blogIndexHtml(posts) )) })) // Individual post pages let app = web.addRoute(app, web.get("/blog/:slug", fn(req: web.Request): web.Response with {File} => { let slug = web.getPathParam(req.path, "/blog/:slug", "slug") match slug { Some(s) => match web.loadPost("./content", "blog", s) { Some(post) => web.htmlResponse(200, web.layout(myLayout, post.title, post.excerpt, web.blogPostHtml(post))), None => web.htmlResponse(404, "Not found") }, None => web.htmlResponse(404, "Not found") } })) ``` ## Guide: Handle Form Submissions ```lux let app = web.addRoute(app, web.post("/api/subscribe", fn(req: web.Request): web.Response with {Sql} => { let fields = web.parseBody(req.body) match web.getField(fields, "email") { Some(email) => match web.validateEmail(email) { Ok(validEmail) => { web.saveSubscriber("data/subscribers.db", validEmail) web.redirect("/thanks") }, Err(msg) => web.htmlResponse(400, msg) }, None => web.htmlResponse(400, "Email required") } })) ``` ## Architecture The framework is built on Lux's algebraic effect system: - **`HttpServer` effect** — Accepts connections and sends responses - **`File` effect** — Reads static files and markdown content - **`Sql` effect** — SQLite database for form submissions - **`Console` effect** — Request logging Effects are declared in function signatures, making it clear what each handler does. For testing, swap effect handlers to mock the database, filesystem, or HTTP server.