App Builder

Builder pattern to configure and launch. Connects the Database, ServerConfig, Middleware pipeline, and async startup hooks.

use floz::prelude::*;

#[floz::main]
async fn main() -> std::io::Result<()> {
    App::new()
        .config(Config::from_env())           // loaded automatically if omitted
        .server(ServerConfig::new()
            .with_default_port(8080)
            .with_middleware(RequestTrace::default())
            .with_middleware(Compression::gzip())
        )
        .on_start(|ctx| async move {
            // 1. Cache pre-warming
            // 2. Spawning tokio::spawn background workers
            // 3. Third-party handshakes (Consul, Redis)
            info!("Worker loop initialized");
        })
        .run().await                          // auto-discovers #[route]s
}

AppContext

The injection container passed automatically to route handlers:

pub struct AppContext {
    pub db_pool: DbPool,
    pub config: Config,
    pub extensions: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
}

Configuration

All config loaded from environment variables. No YAML, no TOML.

Env VariableRequiredDefaultDescription
DATABASE_URLPostgreSQL connection string
HOST127.0.0.1Server bind host
PORT3030Server bind port
SERVER_ENVDEVDEV / STAGING / PROD
REDIS_URLRedis (workers)
JWT_TOKENJWT signing secret
ECHODebug echo logging
let config = Config::from_env();    // loads .env automatically
let config = Config::global();      // singleton
if config.is_dev() { /* dev-only */ }
let key = Config::require("MY_API_KEY");  // panics if missing

Zero-Cost Middleware

floz features a static-dispatch chained middleware pipeline instead of dynamically boxed futures. This allows the compiler to fully inline requests yielding zero overhead.

ServerConfig::new()
    .with_middleware(Cors::permissive())
    .with_middleware(RequestTrace::default())
    .with_middleware(CacheMiddleware::default())
    .with_middleware(Compression::gzip().level(3))

New: CacheMiddleware provides high-performance response caching orchestrated perfectly with your #[route(cache(...))] rules. RequestTrace adds structured logger spans for duration tracking. Compression applies GZip formatting based on client Accept-Encoding seamlessly.

Error Handling

Structured error types with automatic conversions from sqlx, serde, JWT, Redis, UUID, and anyhow.

// Automatic From conversions:
let err: ApiError = sqlx_error.into();       // 30+ variants mapped
let err: ApiError = serde_json_err.into();   // → BadRequest
let err: ApiError = jwt_error.into();        // → specific JWT error

// Convenience constructors
ApiError::bad_request("Invalid email");
ApiError::not_found("User not found");
ApiError::forbidden("Insufficient permissions");
ApiError::internal("Something went wrong");