Chuyển tới nội dung chính

riverbase_http

riverbase_httpHTTP · v0.2.0

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?;
MethodPurpose
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_coupledLower-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_roleAuthorization setup
register_tenant_access_policy, set_domain_tenant_lookupTenancy setup
with_cors, with_cors_origins, without_corsCORS. None is added by default.
with_log_http_requestsPer-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:

POST/api/{ns}/{cmdkey}:post/{resource}collection command
POST/api/{ns}/{cmdkey}:exec/{resource}/{id}object command
GET/api/{ns}/{cmdkey}.metacommand metadata and payload schema
GET/api/{ns}/domain.metacommand discovery for the namespace
GET/api/{ns}/{resource}.listlist
GET/api/{ns}/{resource}.item/{id}item
GET/api/{ns}/{resource}.metaresource metadata
POST/api/{ns}/{report}.reptreport

Scoped 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​