riverbase_http
RiverbaseApp, generated routes, OpenAPI, JWT/OIDC auth, Casbin, WebSocket, SSE, RxDB.
cargo add riverbase_http --git https://github.com/riverbase-io/riverbase- authdefault
- websocket
- rxdb
- sse
- cfgfetch
riverbase_http turns domains into an axum service. It generates command and query routes, serves
OpenAPI, and applies authentication, Casbin, CORS, body limits, security headers, and request
logging. It also hosts the realtime adapters. Domain crates do not depend on it, except optionally
for custom routes.
RiverbaseApp
The application builder:
use riverbase_core::datastore::PgDataStore;
use riverbase_core::domain::DomainRuntime;
use riverbase_http::RiverbaseApp;
let mut app = RiverbaseApp::init("Todo Service", "Todo command and query API")?;
let runtime = DomainRuntime::from_config(app.config()).await?;
let domain = TodoDomain::<PgDataStore>::spawn(&runtime).await?.into_arc();
app.mount_domain(domain).await?;
app.mount_auth_routes().await?;
app.serve().await?;
| Method | Purpose |
|---|---|
init(title, description) | Load config, initialize logging, prepare the OpenAPI document |
init_portal(title, description, bind_env, default_bind, policy_pack) | Same, and also build the DomainRuntime |
with_config(config, …) / from_runtime(runtime, …) | Build from values you already have, for tests |
config(), runtime(), bind_addr() | Accessors |
migrate_only() | true when started with --migrate-only |
mount_domain(Arc<D>) | Command, query, and custom routes; registers on the invokers |
mount_command, mount_query, mount_coupled | Lower-level mounting for split processes |
mount_auth_routes(), mount_auth_routes_with(provider), mount_auth() | Auth JSON routes and the browser flow |
merge(ApiRouter) | Extra OpenAPI-documented routes |
register_routes(Router, RouteAccess), register_public_routes(Router, &[paths]) | Extra plain routes with an explicit access decision |
with_casbin_policy, with_casbin_known_roles, require_any_role | Authorization setup |
register_tenant_access_policy, set_domain_tenant_lookup | Tenancy setup |
with_cors, with_cors_origins, without_cors | CORS. None is added by default. |
with_log_http_requests | Per-request access logs |
require_posture(Posture) | Turn the secure-defaults checklist into startup checks |
bind(addr), into_router(), into_axum(), serve() | Finalize and run |
riverbase_http::report_fatal(err) logs a startup error through tracing and exits with status 1.
That is easier to read than the default Debug print from returning Err in main.
Generated routes
For each mounted domain:
/api/{ns}/{cmdkey}:post/{resource}collection command/api/{ns}/{cmdkey}:exec/{resource}/{id}object command/api/{ns}/{cmdkey}.metacommand metadata and payload schema/api/{ns}/domain.metacommand discovery for the namespace/api/{ns}/{resource}.listlist/api/{ns}/{resource}.item/{id}item/api/{ns}/{resource}.metaresource metadata/api/{ns}/{report}.reptreportScoped variants, signed :link and :hook routes, and API zones are covered in
HTTP conventions.
Middleware
into_router() assembles the middleware when the router is finalized:
- security headers and a request correlation id;
- body size limits (
request_body_max_bytes,request_upload_max_bytes); - CORS, only when you configure it;
- sessions and authentication (feature
auth); - Casbin route authorization, when
[riverbase.casbin] enabled = true; - optional per-request logging.
Portal composition
A process that serves several products' domains composes them as application modules:
use riverbase_http::{ApplicationModule, CompositionOutcome, PortalComposer, PortalSpec};
struct TodoModule;
#[async_trait::async_trait]
impl ApplicationModule for TodoModule {
fn name(&self) -> &'static str { "todo" }
// `prepare(&self, runtime)` has a default no-op; override it to run setup such as
// migrations before any module mounts routes.
async fn mount(&self, portal: &mut PortalComposer) -> RiverbaseResult<()> {
let domain = TodoDomain::<PgDataStore>::spawn(portal.runtime()).await?.into_arc();
portal.mount_domain(domain).await
}
}
let spec = PortalSpec::new("Portal", "All products", "PORTAL_BIND_ADDR", "0.0.0.0:8080");
match PortalComposer::initialize(spec).await?.compose([Arc::new(TodoModule) as _]).await? {
CompositionOutcome::MigrationsComplete => {}
CompositionOutcome::Ready(portal) => portal.serve().await?,
}
prepare always runs, including in --migrate-only mode. mount never runs in migrate-only mode.
Mounting the same namespace twice fails with APP-003.
Sub-pages
- Authentication: providers, sessions, profiles
- Authorization: Casbin model, policy, activities
- OpenAPI: the generated document and its extensions
- Realtime: WebSocket, SSE, RxDB