- 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>
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_URLenv set duringcargo build(live DB) — convenient in dev, brittle in CI.- Offline mode (recommended for CI):
cargo sqlx preparecommits.sqlx/query-*.jsonto the repo, then CI builds withSQLX_OFFLINE=trueand 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.