# 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
```rust
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`][ctx]; the body, headers, query
parameters, and multipart files travel on it:
```rust
// 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");
```
[ctx]: https://docs.rs/shared-framework/latest/shared_framework/logging/struct.CorrelationContext.html
## 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:
```rust
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
```rust
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
```rust
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
```bash
cargo check
cargo build
cargo test
cargo doc --no-deps
```
[Hyper]: https://hyper.rs
[SeaORM]: https://www.sea-ql.org/SeaORM/