modo-0.3.1 has been yanked.
modo
Ergonomic Rust web framework for small monolithic apps. Single binary, compile-time route discovery, batteries included.
Features
| Feature | Enables |
|---|---|
templates |
MiniJinja template engine, ViewRenderer, ViewResponse, #[view], #[template_function], #[template_filter] |
csrf |
Double-submit cookie CSRF protection middleware and CsrfToken extractor |
i18n |
Translation store, I18n extractor, #[t] macro |
sse |
Server-Sent Events (SseEvent, SseBroadcastManager, Sse extractor) |
static-fs |
Filesystem static file serving (development) |
static-embed |
Embedded static files via rust-embed (production) |
Usage
Minimal application
use ;
async
async
async
Handlers with validation and sanitization
use ;
use ;
async
Grouping routes into a module
async
Registering services
use HandlerResult;
use Db;
async
async
Cookies
use CookieManager;
async
async
CORS
use CorsConfig;
async
Graceful shutdown
use GracefulShutdown;
async
Configuration
Config is loaded from config/{MODO_ENV}.yaml (default: config/development.yaml).
${VAR} and ${VAR:-default} patterns in the YAML are substituted from environment variables.
server:
port: 3000
host: "0.0.0.0"
secret_key: "${SECRET_KEY}"
log_level: "info"
shutdown_timeout_secs: 30
hook_timeout_secs: 5
http:
timeout: 30 # request timeout in seconds; omit to disable
body_limit: "2mb"
compression: false
catch_panic: true
trailing_slash: none # none | strip | add
maintenance: false
sensitive_headers: true
security_headers:
enabled: true
hsts: true
hsts_max_age: 31536000
rate_limit:
requests: 100
window_secs: 60
cookies:
path: "/"
secure: true
http_only: true
same_site: lax # strict | lax | none
Key Types
| Type | Purpose |
|---|---|
AppBuilder |
Fluent builder — register services, layers, and call .run() |
AppState |
Shared axum state — services, server config, cookie key |
ServiceRegistry |
Type-map for application services |
AppConfig |
Top-level YAML config (deserialized by #[modo::main]) |
HttpError |
Ergonomic HTTP 4xx/5xx error enum with IntoResponse |
Error |
Structured error with status, code, message, and details |
HandlerResult<T> |
Result<T, Error> alias for generic handlers |
JsonResult<T> |
Result<Json<T>, Error> alias for JSON API handlers |
CookieManager |
Plain, signed, and encrypted cookie read/write extractor |
JsonReq<T> |
JSON request extractor with auto-sanitization |
FormReq<T> |
Form request extractor with auto-sanitization |
PathReq<T> |
Path parameter extractor (re-export of axum::extract::Path) |
QueryReq<T> |
Query string extractor (re-export of axum::extract::Query) |
RequestId |
ULID request ID injected by middleware and propagated via headers |
ClientIp |
Resolved client IP (supports trusted proxies and Cloudflare) |
RateLimitInfo |
Rate limit header info available as a request extractor |
GracefulShutdown |
Trait for services that participate in shutdown sequencing |
ShutdownPhase |
Drain (before user hooks) or Close (after user hooks) |
CorsConfig |
CORS policy (mirror, list, custom, any) |
Feature-gated types
| Type | Feature | Purpose |
|---|---|---|
ViewRenderer |
templates |
Explicit template rendering with HTMX detection |
ViewResponse |
templates |
Handler return type for rendered HTML or redirects |
ViewResult |
templates |
Result<ViewResponse, Error> alias |
CsrfToken |
csrf |
CSRF token injected by middleware into request extensions |
I18n |
i18n |
Translation extractor with per-request language resolution |
SseEvent |
sse |
Builder for a single SSE event |
SseBroadcastManager |
sse |
Keyed fan-out broadcast channels |
SseResponse |
sse |
Handler return type wrapping an SSE stream |
Sse |
sse |
Extractor that applies keep-alive config to SSE responses |