Expand description
§modo
A Rust web framework for small monolithic apps. Single crate, zero proc
macros, built on axum 0.8 with libsql
(SQLite) for persistence. Handlers are plain async fn, routes use
axum::Router directly, services are wired explicitly in main(), and
database queries use raw libsql. Current version: 0.11.0.
§Modules
Foundation:
| Module | Purpose |
|---|---|
config | YAML config loader with ${VAR} / ${VAR:default} env substitution |
error | Framework Error type (status + message + source + code) and Result alias |
runtime | Graceful-shutdown Task abstraction used by run! |
server | HTTP server bootstrap with bind, listen, and shutdown wiring |
service | Typed service Registry and AppState |
Persistence & I/O:
| Module | Purpose |
|---|---|
cache | In-memory LRU cache with TTL |
db | libsql/SQLite Database handle, migrations, query helpers |
storage | S3-compatible object storage with ACL and upload-from-URL |
Request lifecycle:
| Module | Purpose |
|---|---|
client | HTTP client with shared connection pool |
cookie | Signed/private cookie helpers and config |
extractor | Request extractors with auto-sanitisation (JSON, form, query, multipart) |
flash | Signed read-once cookie flash messages |
ip | ClientIp extractor with trusted-proxy resolution |
middleware | Tower middleware (CORS, CSRF, rate limit, security headers, etc.) |
sse | Server-Sent Events with named broadcast channels |
Identity & multi-tenancy:
| Module | Purpose |
|---|---|
auth | Sessions (cookie + JWT), passwords, TOTP, OAuth2, API keys, RBAC roles |
tenant | Multi-tenancy via subdomain, header, path, or custom resolver |
tier | Subscription-tier gating |
Background work:
| Module | Purpose |
|---|---|
cron | 5/6-field cron scheduler |
job | SQLite-backed job queue with retries, scheduling, and idempotent enqueue |
Application services:
| Module | Purpose |
|---|---|
email | Markdown-to-HTML email rendering with SMTP |
i18n | ICU plural rules, locale resolution, translation store |
qrcode | QR code rendering (SVG / PNG) |
template | MiniJinja with i18n, HTMX detection, flash integration |
webhook | Outbound webhook delivery with Standard Webhooks signing |
Observability:
| Module | Purpose |
|---|---|
audit | Structured audit-log writer |
health | /_live and /_ready endpoint handlers |
tracing | Tracing/Sentry subscriber setup and request-trace middleware |
Network primitives:
| Module | Purpose |
|---|---|
dns | TXT/CNAME verification for custom-domain validation |
embed | Embed.ly-style URL preview |
geolocation | MaxMind GeoIP2 lookup with middleware |
Utilities:
| Module | Purpose |
|---|---|
encoding | Base64url, hex, and other encoding helpers |
id | id::ulid() (26-char) and id::short() (13-char base36) |
sanitize | Sanitize trait used by extractors |
validate | Validate, Validator, ValidationError |
Virtual flat-indexes (cross-module re-exports for discoverability):
| Module | Purpose |
|---|---|
extractors | Every public request extractor, gathered from across the crate |
guards | Every route-level gating layer applied via .route_layer() |
middlewares | Every public middleware constructor |
prelude | Glob-import bundle for handler modules |
Test-only (gated behind the test-helpers feature):
| Module | Purpose |
|---|---|
testing | TestDb, TestApp, TestSession, and in-memory/stub backends |
§Re-exports
Crate-root re-exports for the items used in almost every program:
Error— framework error type (HTTP status + message + optional source/code)Result—std::result::Result<T, Error>aliasConfig— top-level application configuration (config::Config)run!— macro that waits for SIGTERM/SIGINT then shuts down each suppliedTaskin declaration order
Plus the four dependency crates whose types appear in handler signatures, so you don’t need to pin matching versions yourself:
axum— router, extractors, responsesserde—Serialize/Deserializederivesserde_json— JSON values and macrostokio— runtime, tasks, sync primitives
§Quick start
Add to Cargo.toml:
[dependencies]
modo = { package = "modo-rs", version = "0.11.0" }
[dev-dependencies]
modo = { package = "modo-rs", version = "0.11.0", features = ["test-helpers"] }Minimal application:
use modo::axum::{Router, routing::get};
use modo::{Config, Result};
async fn hello() -> &'static str {
"Hello, modo!"
}
#[tokio::main]
async fn main() -> Result<()> {
let config: Config = modo::config::load("config/")?;
let app = Router::new().route("/", get(hello));
let server = modo::server::http(app, &config.server).await?;
modo::run!(server).await
}Inside a handler module, pull in the common handler-time types with:
use modo::prelude::*;§Features
Every production module is always compiled — there are no per-capability
cargo features. The only flag is test-helpers, which exposes in-memory
backends and test harnesses; enable it in your [dev-dependencies].
| Feature | Purpose |
|---|---|
test-helpers | Enables the testing module (TestDb, TestApp, TestSession) and all in-memory/stub backends |
Re-exports§
pub use config::Config;pub use error::Error;pub use error::Result;pub use axum;pub use serde;pub use serde_json;pub use tokio;
Modules§
- audit
- modo::audit
- auth
- modo::auth
- cache
- modo::cache
- client
- modo::client
- config
- modo::config
- cookie
- modo::cookie
- cron
- modo::cron
- db
- modo::db
- dns
- modo::dns
- modo::email
- embed
- modo::embed
- encoding
- modo::encoding
- error
- modo::error
- extractor
- modo::extractor
- extractors
- Flat index of every axum extractor modo ships.
- flash
- modo::flash
- geolocation
- modo::geolocation
- guards
- Flat index of every route-level gating layer.
- health
- modo::health
- i18n
- modo::i18n
- id
- modo::id
- ip
- modo::ip
- job
- modo::job
- middleware
- modo::middleware
- middlewares
- Flat index of every Tower Layer modo ships.
- prelude
- Handler-time prelude.
- qrcode
- modo::qrcode
- runtime
- modo::runtime
- sanitize
- modo::sanitize
- server
- modo::server
- service
- modo::service
- sse
- modo::sse
- storage
- modo::storage
- template
- modo::template
- tenant
- modo::tenant
- tier
- modo::tier
- tracing
- modo::tracing
- validate
- modo::validate
- webhook
- modo::webhook
Macros§
- run
- Waits for a shutdown signal and then shuts down each task in order.