post-construct-migrations

Runs database migrations automatically in a

SKILL.md

GitHub

Database Migrations in post_construct

Pattern

use injectable::prelude::*;
use sqlx::{Pool, Sqlite};

#[injectable(factory)]
async fn make_pool(cfg: Inject<AppConfig>) -> Result<Pool<Sqlite>, sqlx::Error> {
    sqlx::sqlite::SqlitePoolOptions::new()
        .max_connections(1)
        .idle_timeout(None)
        .max_lifetime(None)
        .connect(&cfg.database_url).await
}

#[injectable]
pub struct UserRepository {
    #[injectable(inject(use_factory_async = self::make_pool))]
    pool: Pool<Sqlite>,
}

#[injectable]
impl UserRepository {
    #[injectable(post_construct)]
    async fn migrate(&self) -> Result<(), sqlx::Error> {
        sqlx::query(
            "CREATE TABLE IF NOT EXISTS users (
                id         INTEGER PRIMARY KEY AUTOINCREMENT,
                email      TEXT    NOT NULL UNIQUE,
                name       TEXT    NOT NULL,
                created_at TEXT    NOT NULL DEFAULT (datetime('now'))
            )",
        )
        .execute(&self.pool)
        .await?;
        println!("[UserRepository] Schema ready");
        Ok(())
    }

    #[injectable(pre_destruct)]
    async fn shutdown(&self) {
        self.pool.close().await;
    }
}

Multiple services migrate their own tables

#[injectable]
impl OrderRepository {
    #[injectable(post_construct)]
    async fn migrate(&self) -> Result<(), sqlx::Error> {
        sqlx::query("CREATE TABLE IF NOT EXISTS orders ( … )").execute(&self.pool).await?;
        Ok(())
    }
}

Triggering migrations

Migrations run automatically when the service is first resolved. To run eagerly at startup:

let container = Container::builder().build().await?;
let ctx = container.context();

// Warm up all repos — triggers their migrations
ctx.extract::<Inject<UserRepository>>().await?;
ctx.extract::<Inject<OrderRepository>>().await?;

println!("All schemas ready");

Using sqlx migrate! macro (alternative)

#[injectable(post_construct)]
async fn migrate(&self) -> Result<(), sqlx::migrate::MigrateError> {
    sqlx::migrate!("./migrations").run(&self.pool).await
}

Requires migration files in ./migrations/*.sql.