async-initialization
Initializes services that require async work at startup — connecting to databases, loading config from remote, seeding caches. Use when
SKILL.mdskills/async-initialization/SKILL.md
GitHubAsync Initialization
Which async macro to use?
| Pattern | Best for | Example macro |
|---|---|---|
| Async constructor | The service itself owns the async setup | #[injectable(ctor)] async fn new(...) |
| Async external factory | A third-party type such as sqlx::SqlitePool must be created first |
#[injectable(inject(use_factory_async = self::make_db_pool))] |
| Post-construction warm-up | The service exists, then needs migrations, cache loads, or probes | #[injectable(post_construct)] |
For async database pools, prefer a dedicated #[injectable(factory)] helper plus
use_factory_async instead of putting connection logic directly inside an
unrelated service constructor.
Async constructor
use injectable::prelude::*;
#[derive(Clone)]
struct DbPool { inner: sqlx::SqlitePool }
#[injectable]
impl DbPool {
#[injectable(ctor)]
pub async fn new(cfg: Inject<AppConfig>) -> Result<Self, sqlx::Error> {
let pool = sqlx::SqlitePool::connect(&cfg.db_url).await?;
Ok(Self { inner: pool })
}
}
Async post_construct
#[injectable]
impl CacheService {
#[injectable(ctor)]
fn new(#[injectable(inject)] db: Arc<Database>) -> Self {
Self { db, cache: DashMap::new() }
}
#[injectable(post_construct)]
async fn warm_up(&self) -> HookResult {
let items = self.db.load_hot_items().await
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
for item in items {
self.cache.insert(item.id, item);
}
println!("[Cache] Loaded {} items", self.cache.len());
Ok(())
}
}
Zero-arg async constructor
struct Clock { started: std::time::Instant }
#[injectable]
impl Clock {
#[injectable(ctor)]
pub async fn new() -> Self {
// Simulate async init (e.g., NTP sync)
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
Self { started: std::time::Instant::now() }
}
pub fn elapsed(&self) -> std::time::Duration {
self.started.elapsed()
}
}
Eager warm-up ensures async init completes before serving
let container = Container::builder().build().await?;
let ctx = container.context();
// These calls trigger async constructors + post_construct hooks
ctx.extract::<Inject<DbPool>>().await?;
ctx.extract::<Inject<CacheService>>().await?;
println!("All services initialized — ready to serve");
// Start Axum server here…