Connect a second database
Goal: query a second (or third+) database from a controller, alongside the app’s primary ctx.db connection — for example, a read replica, a legacy database, or per-tenant databases.
Loco ships a ready-made initializer for this: MultiDbInitializer, a named map of connections. It lives under loco_rs::initializers::multi_db and is gated behind the with-db feature. Each config entry accepts the same keys as the primary database: block — see Configuration § database for the full list (uri, enable_logging, min_connections, max_connections, connect_timeout, idle_timeout, acquire_timeout, auto_migrate, dangerously_truncate, dangerously_recreate, run_on_start).
Configure each named database
Add one or more entries under a multi_db map, nested under the top-level initializers key in your environment config. A single extra connection is just a one-entry map:
initializers: multi_db: secondary_db: uri: postgres://loco:loco@localhost:5432/loco_app enable_logging: false connect_timeout: 500 idle_timeout: 500 min_connections: 1 max_connections: 1 auto_migrate: false dangerously_truncate: false dangerously_recreate: falseAdd more entries to open more connections:
initializers: multi_db: secondary_db: uri: postgres://loco:loco@localhost:5432/loco_app enable_logging: false connect_timeout: 500 idle_timeout: 500 min_connections: 1 max_connections: 1 auto_migrate: false dangerously_truncate: false dangerously_recreate: false third_db: uri: postgres://loco:loco@localhost:5432/loco_app_reporting enable_logging: false connect_timeout: 500 idle_timeout: 500 min_connections: 1 max_connections: 1 auto_migrate: false dangerously_truncate: false dangerously_recreate: falseRegister the initializer
use loco_rs::app::{AppContext, Initializer};
async fn initializers(_ctx: &AppContext) -> Result<Vec<Box<dyn Initializer>>> { let initializers: Vec<Box<dyn Initializer>> = vec![ Box::new(loco_rs::initializers::multi_db::MultiDbInitializer), ];
Ok(initializers)}Look connections up by name
MultiDbInitializer layers a loco_rs::db::MultiDb (a thin HashMap<String, DatabaseConnection> wrapper) as an axum Extension:
use sea_orm::EntityTrait;use axum::{response::IntoResponse, Extension};use loco_rs::db::MultiDb;
pub async fn list( State(ctx): State<AppContext>, Extension(multi_db): Extension<MultiDb>,) -> Result<impl IntoResponse> { let third_db = multi_db.get("third_db")?; let res = Entity::find().all(third_db).await; format::json(res)}multi_db.get(name) returns an error if that key isn’t configured — no silent None/panic.
Result
ctx.db remains your app’s primary connection (used for auto-migration, boot-time checks, etc.); the extra connection(s) arrive purely through the MultiDb axum Extension and only in handlers that ask for them.
Migrating from extra_db
ExtraDbInitializer has been removed in favor of MultiDbInitializer. To migrate:
- Config: former
initializers.extra_db: { ... }becomesinitializers.multi_db: { <name>: { ... } }— pick a name for your connection and nest the same keys under it. - Registration: swap
Box::new(loco_rs::initializers::extra_db::ExtraDbInitializer)forBox::new(loco_rs::initializers::multi_db::MultiDbInitializer). - Handlers: change
Extension(db): Extension<DatabaseConnection>toExtension(multi_db): Extension<MultiDb>, then look up the connection withlet db = multi_db.get("<name>")?;.
Next
- Configuration reference for every key a
database:-shaped block accepts. - Add middleware for how the
initializers/middleware hooks work.