Skip to content

SQLx

Databases & CachingDatabaseRust

What it is

SQLx is an async, pure-Rust SQL toolkit that verifies your queries against a real database at compile time, without being an ORM.

Use the query! macros for compile-time checked SQL, or the unchecked functions for dynamic queries. Migrations are managed by sqlx-cli.

Installation

cargo add sqlx --features runtime-tokio,postgres,macros

Getting started

The smallest useful thing you can do with it, and what each part means.

Compile-time verified queries
// DATABASE_URL must be set at build time for these macros.
let book = sqlx::query_as!(
    Book,
    "SELECT id, title, year FROM books WHERE id = $1",
    id
)
.fetch_optional(&pool)
.await?;

// Misspell a column and this fails to COMPILE, not at runtime:
// error: column "titel" does not exist
This is SQLx's central benefit — a typo or a dropped column is a build error. It requires a reachable database during compilation, which is the main operational cost.
Pool setup and inserts
let pool = sqlx::postgres::PgPoolOptions::new()
    .max_connections(20)
    .acquire_timeout(Duration::from_secs(5))
    .connect(&database_url)
    .await?;

let id: i64 = sqlx::query_scalar!(
    "INSERT INTO books (title, year) VALUES ($1, $2) RETURNING id",
    title, year
)
.fetch_one(&pool)
.await?;
RETURNING avoids a second round trip for the generated id, and query_scalar types the result as the single column rather than a struct.

Advanced usage

Where the library earns its place over a simpler alternative.

Transactions and offline builds
let mut tx = pool.begin().await?;

sqlx::query!("UPDATE stock SET quantity = quantity - $1 WHERE sku = $2", qty, sku)
    .execute(&mut *tx)
    .await?;

sqlx::query!("INSERT INTO orders (sku, qty) VALUES ($1, $2)", sku, qty)
    .execute(&mut *tx)
    .await?;

tx.commit().await?;   // dropping without commit rolls back

// For CI without a database:
//   cargo sqlx prepare      -> writes .sqlx/ query metadata
//   SQLX_OFFLINE=true cargo build
Offline mode is essential for CI and Docker builds. Commit the .sqlx directory and regenerate it whenever a query changes, or the build will fail.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

set DATABASE_URL to use query macros
The macros need a database at build time. Set the variable, or run cargo sqlx prepare and build with SQLX_OFFLINE=true.
error occurred while decoding column
The Rust type does not match the SQL type — commonly i32 against BIGINT, or a non-Option field against a nullable column.

Best practices

  • Commit the .sqlx metadata and build with SQLX_OFFLINE in CI.
  • Prefer query_as! over query_as — only the macro is verified.
  • Set acquire_timeout on the pool so a saturated database fails fast rather than hanging.
  • Use transactions for multi-statement invariants; a dropped transaction rolls back automatically.

Background

Why it exists, and what it was reacting to.

SQLx's distinguishing feature is compile-time verification: the macros connect to your development database during cargo build and check that the SQL parses, the columns exist and the Rust types match.