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#
servereads the request, with a 10-second read timeout and a 1 MiB body limit (MAX_BODY, larger bodies get413).- Global middleware runs, in the order it was added: security headers, request logging.
- The first route whose method and path match runs, wrapped in its group's middleware.
- No route matched: a
GETserves the file of that name frompublic/; anything else is404. - Errors render as pages, and a handler that panics answers
500and is logged instead of stopping the server.
Everything in the box#
| Feature | API | Page |
|---|---|---|
| Routes and parameters | Router::get, post, route, view, {name} | Routing |
| Requests and responses | Request::query, form, param; json, redirect, Response | Routing |
| Middleware and groups | Router::middleware, group, Next, security_headers | Middleware |
| Sessions, flash, CSRF | Sessions, Session, csrf() | Sessions and CSRF |
| Login and roles | auth::Auth, authenticate, role, allows, User | Login and roles |
mail::Mailer, Message, Recipient, MAIL_* | ||
| Pages and assets | view, render, escape, error, asset | Views and assets |
| Rate limits | Throttle, throttle | Rate limiting |
| Settings and logs | Config, Log, Logger | Config and logging |
| Tests without a server | App::test, testing::Client | Testing |
| Going live | PORT, nginx, LOG_CHANNEL=stderr | Deployment |
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.