Maw
A minimal web framework for Rust. No macros, just functions.
Why
- No macros. Routes are just functions, nothing hidden behind attributes.
c.next().awaitworks like Express/Fiber middleware. Call it, then inspect or modify the response after.println!("{router:#?}")prints every route with its handlers and, in debug builds, source location.- Handlers can be closures, plain functions, or structs.
Quick Start
use *;
async
Middleware
Call next(), do work before and after it:
new
.middleware
.get
Or scope it to one route:
.get
Three Ways to Write Handlers
// Closure
.get
.get
// Function
async
.get
// Struct, for handlers that carry their own state
.middleware
Passing Extra Data Into a Handler
WithState lets you hand a closure some extra value without writing a struct for it:
.get
No separate handler type, no trait impl, just a closure that takes one more argument. The value needs Clone + Send + Sync + 'static.
Request Data
async |c: &mut Ctx|
c.req.locals is a typed per-request map (AnyMap) for stashing values between middleware and handlers. c.res.locals does the same for template context.
Route Groups
let api = group
.get
.post;
let admin = group
.middleware
.get;
new
.push
.push
Features
[]
= { = "X.Y", = ["minijinja", "websocket"] }
| Feature | What |
|---|---|
minijinja |
Template rendering |
xml |
XML request/response support |
websocket |
WebSocket support |
static_files |
Serve embedded files, with optional SPA fallback |
middleware-cookie |
Cookie parsing/setting (plain, signed, encrypted) |
middleware-session |
Session management, pluggable storage backend |
middleware-csrf |
CSRF protection (cookie or session backed) |
middleware-logging |
Request logging |
middleware-catch_panic |
Panic recovery, with optional custom handler |
middleware-body_limit |
Per-request body size limits |
middleware |
All middleware features |
full |
Everything |
Check Cargo.toml for the current version. Don't copy the X.Y above literally.
Templates
let app = new
.views
.router;
Static Files
new.static_files
fallback_to serves that file when a path doesn't match anything embedded, useful for SPAs. max_age sets Cache-Control. Last-Modified / If-Modified-Since are handled for you.
WebSocket
.ws
Server-Sent Events
use Bytes;
use stream;
.get
Errors
Return Result<T, StatusError> from a handler and Maw turns it into the right response:
async |c: &mut Ctx|
StatusError has a constructor for every standard status code, plus .brief(), .detail(), and .error() to attach more context.
Debug Your Routes
let router = new
.middleware
.push
.push;
println!;
License
MIT