shared-framework 0.0.16

Reusable building blocks for HTTP services — Hyper routing, SeaORM data layer, validation, OpenAPI docs, jobs, queues, cache.
Documentation

shared-framework

Reusable building blocks for HTTP services in Rust: routing and controllers on Hyper, a SeaORM data layer with repositories and explicit relation hydration, request validation, live OpenAPI 3.1 docs with a bundled Swagger UI, background jobs, RabbitMQ consumers, Redis/in-memory caching, and resilience helpers (retries, circuit breakers, rate limiting).

Quick start

use std::sync::Arc;
use shared_framework::{
    AppEnvironment, ConfigurationRegistrant,
    controller::{RouteController, RouteControllerExt, RouteDescription, Router},
    logging::CorrelationContext,
    response::ServiceResult,
};

struct HealthController;

#[async_trait::async_trait]
impl RouteController for HealthController {
    fn base_path(&self) -> &str { "/health" }

    async fn register_routes(&self, router: &mut Router) {
        self.mount_get(
            router,
            "/",
            RouteDescription::new("Health check").group("Ops"),
            |_ctx: CorrelationContext| async move {
                Ok(ServiceResult::ok("ok", serde_json::json!({ "up": true })))
            },
            vec![],
        );
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    AppEnvironment::with_env_file(None)?;
    let registrant = Arc::new(ConfigurationRegistrant::new("0.0.0.0:8080".parse()?));
    registrant.mount_controller(HealthController).await;
    registrant.serve().await?;
    tokio::signal::ctrl_c().await?;
    Ok(())
}

Handlers receive only a CorrelationContext; the body, headers, query parameters, and multipart files travel on it:

// JSON or form DTO, deserialized and validated in place:
let input: CreateUser = ctx.body()?;
// Raw payloads (text/plain routes):
let raw: String = ctx.body_string()?;
// Headers, query, files:
let token = ctx.header("x-auth-token");
let search = ctx.query_param_or("q", "");
let avatars = ctx.files_for("avatar");

Booting a full service

GenericStartup::bootstrap wires everything optional registrars provide — static assets, controllers (plus the docs controller), the Basilisk gateway, jobs, consumers, and socket configuration — and exposes the results for manual wiring of anything you skip:

use std::sync::Arc;
use shared_framework::app::{GenericStartup, StartupOptions};

let startup = GenericStartup::bootstrap(
    StartupOptions::default().with_env_file(".env"),
    vec!["/api".to_string()],
    None,                              // static assets
    Some(Arc::new(MyControllers)),     // impl ControllerRegistrar
    None,                              // queue consumers
)
.await?;
startup.serve().await?;

Registrars build their pieces from the startup state (registrant(), gateway_client(), redis_client(), server_addr()), and jobs run on demand via startup.run_jobs().await.

Data

use shared_framework::data::{PersistentRepository, QueryData, RepositoryOptions};

let repo = PersistentRepository::<entity::Entity>::new(db);

// Paginated reads; options combine request state with caller overrides.
let page = repo
    .get_paginated_view(
        Some(QueryData::new(entity::Entity::find()).order_by_desc()),
        RepositoryOptions::from_ctx(&ctx).with_limit(20),
    )
    .await?;

// Explicit relation hydration: one batched query per relation, no N+1.
let with_orders = repo
    .get_all_traversed(
        Some(
            QueryData::new(customer::Entity::find()).with_traverser(|tv| async move {
                tv.load_many_as("orders", order::Entity).await?;
                Ok(())
            }),
        ),
        None,
    )
    .await?;

Validation

use shared_framework::validation::{Validate, ValidationException, is_email, is_not_blank};

struct CreateUser {
    email: String,
    name: String,
}

impl Validate for CreateUser {
    fn validate(&self) -> Result<(), ValidationException> {
        is_email("email", &self.email, None)?;
        is_not_blank("name", &self.name, None)?;
        Ok(())
    }
}

Returning ctx.body::<CreateUser>() in a handler runs this automatically and answers 400 on the first failure.

API documentation

Routes self-document through RouteDescription (groups, bodies via DocumentableDTO, response examples, headers, params, rate limits, file uploads). After mounting, GET /docs serves the generated OpenAPI 3.1 JSON and / serves the bundled Swagger UI — hidden in production unless external docs mode is enabled.

Background work

  • Jobs (job): ServiceJob with periodic, exact-time, or combined schedules, run by JobRegistry.
  • Queues (queue): RabbitMQ consumers with retry (.retry) and dead-letter (.dlq) queues plus a message producer, on lapin.
  • Cache (data::cache): region-based in-memory and Redis storage with a shared StorageAccess interface.
  • Resilience (retry, service, middleware::rate): exponential-backoff retries, per-provider circuit breakers, and fixed-window rate limiting (in-memory or Redis-backed).

Configuration

Loaded once from .env (or a given file) plus the process environment. Required keys: SERVICE_NAME, SERVICE_VERSION, BUILD_NUMBER, ENVIRONMENT_KIND (DEVELOPMENT/DEBUG/STAGING/PRODUCTION), SERVER_PORT, SERVICE_URL, POSTGRESQL_URL, REDIS_URL, RABBITMQ_URL, MONGODB_URL. Useful defaults are applied for the rest (PROCESS_ROLE, SERVER_COUNT, WORKER_COUNT, SOCKET_COUNT, SOCKET_PORT, … — see AppEnvironment). Gateway registration additionally needs BASILISK_HOST, BASILISK_BUS_PORT, BASILISK_TOKEN, BASILISK_GATEWAY_URL, BASILISK_SERVICE_ID, and SERVICE_KEY.

Layout

app/            GenericStartup, StartupOptions, registrar traits
controller/     RouteController, Router, RouteDescription, server bootstrap
middleware/     composable handlers: identification, rate limiting, monitoring
data/           BaseEntity, QueryData, RepositoryOptions, PersistentRepository,
                relation hydration (traverse), cache, seeding, connectors
validation/     Validate trait + constraint helpers
logging/        CorrelationContext, loggers (tracing-based)
response/       ServiceResult, ErrorResult, typed results
service/        provider chains with circuit breakers
queue/          RabbitMQ consumers and producers
job/            scheduled background jobs
doc/            OpenAPI generation, docs controller, bundled Swagger UI
gateway/        service registration and event bus client
retry/          backoff retry strategies
types/          shared value types (geo, ranges, batches)
utils/          crypto, dates, HTTP client, multipart parsing, helpers
env/            environment loading and validation
config/         server configuration re-export
monitoring/     monitoring event plumbing

Building

cargo check
cargo build
cargo test
cargo doc --no-deps