Web

Web apps Released

A web app in Clamp is one Rust binary that serves your routes, your pages and your assets. rustclamp::web uses only the standard library: no async runtime to learn, nothing hidden underneath.

Start a project#

curl -fsSL https://rustclamp.com/install.sh | bash   # once
clamp init my-site --web
cd my-site
cp .env.example .env
clamp dev

clamp dev runs npm install the first time, then starts Vite, which rebuilds public/build/ on every save, and the Rust server, which prints its address: the first free port from 8080, or PORT if set. Refresh the browser to see changes. The front end needs Node.js and npm; the server does not.

Project layout#

app/                             everything the app is made of
  lib.rs                         the map: declares every module (no mod.rs files)
  main.rs                        starts the server with settings from .env
  config/app.rs                  typed settings; .env.example lists the keys
  routes/web.rs, routes/api.rs   pages, and JSON behind a rate limit
  http/controllers/              functions that turn requests into responses
  http/middleware/               request_log.rs logs every request
  http/requests/                 form input and its validation
  models/                        data the app works with
  resources/views/               pages; errors/4xx.html and 5xx.html are the error pages
  resources/css, resources/js    Tailwind 4
.env                             this machine's settings (not in git)
storage/logs/app.log             written at runtime
public/                          web root; public/build/ is generated by Vite
tests/                           cargo test

The app is declared once in app/lib.rs as a web::App: its config functions, migrations, seeders and routes. Clamp opens and migrates the database, shares it and the disks with handlers and adds the security headers, so the entry point is one line:

fn main() {
    my_site::app().run();
}

How a request flows#

  1. serve reads the request, with a 10-second read timeout and a 1 MiB body limit (MAX_BODY, larger bodies get 413).
  2. Global middleware runs, in the order it was added: security headers, request logging.
  3. The first route whose method and path match runs, wrapped in its group's middleware.
  4. No route matched: a GET serves the file of that name from public/; anything else is 404.
  5. Errors render as pages, and a handler that panics answers 500 and is logged instead of stopping the server.

Everything in the box#

FeatureAPIPage
Routes and parametersRouter::get, post, route, view, {name}Routing
Requests and responsesRequest::query, form, param; json, redirect, ResponseRouting
Middleware and groupsRouter::middleware, group, Next, security_headersMiddleware
Sessions, flash, CSRFSessions, Session, csrf()Sessions and CSRF
Login and rolesauth::Auth, authenticate, role, allows, UserLogin and roles
Mailmail::Mailer, Message, Recipient, MAIL_*Mail
Pages and assetsview, render, escape, error, assetViews and assets
Rate limitsThrottle, throttleRate limiting
Settings and logsConfig, Log, LoggerConfig and logging
Tests without a serverApp::test, testing::ClientTesting
Going livePORT, nginx, LOG_CHANNEL=stderrDeployment

Know the limits#

Built for small, honest web apps first. The server answers on 127.0.0.1 with a fixed pool of WEB_THREADS threads (default 32), sessions live in memory or SQLite, and handlers are synchronous. That fits sites, dashboards and internal tools behind nginx. For high-concurrency APIs, the Axum-based rustclamp-http package adds Tower, streaming and graceful drain.

The web feature of the rustclamp crate enables it, and brings the config and log features with it. clamp init --web sets this up for you.