Skip to content

Concepts

Concepts

Sova is a small Express-like HTTP framework: sova-core owns the request path; plugins add optional middleware and helpers.

Request path

text
accept
  → server/conn (semaphore, JoinSet, hyper auto HTTP/1.1+HTTP/2 + with_upgrades)
  → to_sova_request (+ optional OnUpgrade)
  → CompiledRouter::dispatch
  → root middleware (onion)  // request_id → Observability → logger → …
  → matchit route match (+ MatchedRoute / MatchedRouteCapture)
  → route / mount middleware
  → handler
  → IntoResponse
  → hyper response body

App::build() compiles routes once into a cheap-to-clone Server. Prefer Server::handle in tests so the matcher is not rebuilt per request.

Custom middleware (onion)

Signature: (Request, Next) -> Response where Next is FnOnce(Request) -> Future<Response>.

rust
app.use_middleware(|req: Request, next: Next| async move {
    // before
    let mut res = next(req).await;
    // after — mutate response headers/body wrappers
    res = res.header("x-demo", "1");
    res
});
ScopeAPI
Whole appapp.use_middleware(...)
Mount / grouprouter.use_middleware(...) then app.mount("/x", router)
Explain labelsova::extend::named("auth", mw)
Shared statewith_state(S, …) / extend::with_leaked

Short-circuit by not calling next (return 401 / redirect). Pass data to handlers with req.set(T) / req.get::<T>().

Stateful helpers: with_state(S, |arc, req, next| …) and extend::with_leaked for 'static plugin config. Full walkthrough: Getting started → Custom middleware.

Lifecycle

text
start: compile → on_startup → BackgroundServices → accept
stop:  stop accept → drain connections → stop services → on_shutdown

app.run() parses CLI commands (check, routes, openapi --out, tasks, migrate / seed, …) or starts the server. CLI mode runs startup/shutdown and skips accept.

Health probes

EndpointRoleBehavior
GET /healthzlivenessprocess up; no plugin checks
GET /readyreadinessCheckKind::Ready; else 503

register_check → Ready. register_audit → Audit (CLI check only).

Extension model

rust
pub trait Plugin {
    fn id(&self) -> &'static str;
    fn requires(&self) -> &'static [&'static str] { &[] }
    fn meta(&self) -> PluginMeta { /* … */ }
    fn install(self, app: &mut App);
}

app.install(Cors::new());
app.install(|app| { app.get("/x", handler); });

App-author patterns (routes, validate, auth): Getting started and Examples. Plugin authors: Plugin SDK.

Public surface

SurfaceAudience
crate rootApps: App, Request, Response, Plugin, …
extendPlugins / advanced: handlers, middleware traits, bodies

Ownership

  • sova-core — App/Router/Server, dispatch, listen/drain
  • plugins — optional features; core does not depend on plugins
  • KvStore is not in core — wire via app.state(...)

Share (Cell / Slot)

  • Cell<T: Clone> — counters / flags
  • Slot<T> — ownership handoff for sockets/streams

See examples/misc/share_demo.

Logging

listen / run install a default tracing subscriber (LogConfig::from_env).

ControlDefault
RUST_LOGsova=info
SOVA_LOG=offskip install
SOVA_LOG_STDOUT1
SOVA_LOG_FILEunset
SOVA_LOG_ROTATEsize

Stability

Pre-1.0: breaking changes without a major bump. sova 0.1 tracks sova-core 0.1.