KeiSeiKit-1.0/_blocks/db-sqlx.md
Parfii-bot f884891862 feat(blocks): 5 database blocks — postgres/sqlite/sqlx/drizzle/migration-hygiene
- db-postgres.md: PG17 patterns (indexes, pooling, backup); [E4]
- db-sqlite.md: WAL prod patterns, Turso/LiteFS/D1, FTS5
- db-sqlx.md: Rust compile-time checked queries, offline mode
- db-drizzle.md: TS schema-first, drizzle-kit migrations
- db-migration-hygiene.md: universal up/down, zero-downtime,
  backfill, checksum tracking

All blocks <60 LOC per Constructor Pattern. Version numbers
marked [UNVERIFIED] where exact minor pins are needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 20:31:37 +08:00

1.9 KiB

DB — SQLx (Rust) patterns

Use when the project is Rust and needs a SQL-first (not ORM) query layer with compile-time checking. Pairs with stack-rust-axum, stack-rust-cli. [E4 — expert assessment]

Core versions: sqlx = "0.8" (current as of 2026-04) with features runtime-tokio, tls-rustls, and one of postgres / sqlite / mysql. Never mix runtime-async-std and runtime-tokio — they clash at link time. [UNVERIFIED: verify latest on crates.io before pinning]

Compile-time checked queries:

let row = sqlx::query!("SELECT id, name FROM users WHERE id = $1", user_id)
    .fetch_one(&pool).await?;

Requires either:

  • DATABASE_URL env set during cargo build (live DB) — convenient in dev, brittle in CI.
  • Offline mode (recommended for CI): cargo sqlx prepare commits .sqlx/query-*.json to the repo, then CI builds with SQLX_OFFLINE=true and no DB access.

Connection pool:

let pool = sqlx::postgres::PgPoolOptions::new()
    .max_connections(20)                  // tune to server max_connections / replica count
    .acquire_timeout(Duration::from_secs(3))
    .connect(&database_url).await?;

Single PgPool per process, Arc-cloned into handlers. Don't open per-request.

Migrations:

sqlx::migrate!("./migrations").run(&pool).await?;

Built-in runner reads YYYYMMDDHHMMSS_<name>.sql files. For richer UX (up/down, status, create scaffolding) use the kei-migrate primitive in this kit.

Transactions:

let mut tx = pool.begin().await?;
sqlx::query!("...").execute(&mut *tx).await?;
sqlx::query!("...").execute(&mut *tx).await?;
tx.commit().await?;                       // explicit; Drop = rollback

Forbidden: sqlx::query (non-macro) with untrusted input without bind() — that's string concat, i.e. SQL injection; .unwrap() on DB calls in prod paths; enabling both runtime-tokio and runtime-async-std; committing a live DATABASE_URL to .env.example.