Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/launchbadge/sqlx/llms.txt

Use this file to discover all available pages before exploring further.

SQLx’s macro family verifies your SQL queries against a live database (or a cached offline snapshot) at compile time. The compiler rejects queries with syntax errors, unknown tables or columns, and type mismatches before a single binary is shipped. All macros in this family require the macros feature flag, and they all read the database URL from the DATABASE_URL environment variable at compile time (or from the variable specified in sqlx.toml).
Enable compile-time checking by adding macros to your feature list:
[dependencies]
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "macros"] }
You must have a running database accessible at DATABASE_URL during cargo build, or use cargo sqlx prepare to generate an offline snapshot.

query!

macro_rules! query {
    ($sql:expr $(, $args:expr)* $(,)?) => { ... }
}
Prepares a SQL query at compile time and returns a Map that yields anonymous record structs — one field per selected column. Field names are taken directly from the column names in the result set. The return type is unnameable (the struct is anonymous), so it cannot be stored in a named variable with an explicit type annotation. Use query_as! when you need a named, reusable struct.
let row = sqlx::query!("SELECT id, name FROM users WHERE id = $1", user_id)
    .fetch_one(&pool)
    .await?;

// Fields are accessed directly; types are inferred from the DB schema.
println!("{}: {}", row.id, row.name);

Bind parameters

Bind values are passed as positional arguments after the SQL string. Their Rust types must be compatible with the corresponding SQL parameter types; mismatches are compile errors.
// Multiple bind parameters
let row = sqlx::query!(
    "SELECT * FROM orders WHERE user_id = $1 AND status = $2",
    user_id,
    status,
)
.fetch_optional(&pool)
.await?;

Type override syntax

When the macro infers an unexpected type, you can coerce it with an as cast inside the argument list:
// Tell the macro to treat `ids` as `&[Option<i32>]` rather than `&[i32]`
let rows = sqlx::query!(
    "SELECT id FROM users WHERE id = ANY($1)",
    &ids as &[Option<i32>]
)
.fetch_all(&pool)
.await?;
The as syntax is also required for non-'static references and custom Type implementations that the macro cannot fully infer.
DATABASE_URL must be set and point to a running database at compile time. If it is not set, the build will fail. Use cargo sqlx prepare to generate an offline snapshot that allows building without a live database.

query_as!

macro_rules! query_as {
    ($out_type:ty, $sql:expr $(, $args:expr)* $(,)?) => { ... }
}
Like query!, but maps each row into a named struct $out_type instead of an anonymous record. The struct must have a field for each selected column (matched by name), and each field type must be compatible with the corresponding SQL column type.
#[derive(Debug)]
struct User {
    id: i64,
    name: String,
    email: Option<String>,
}

let users: Vec<User> = sqlx::query_as!(
    User,
    "SELECT id, name, email FROM users ORDER BY id"
)
.fetch_all(&pool)
.await?;
query_as! does not require #[derive(FromRow)] on the struct — it generates the mapping code at compile time and checks column names and types directly.

query_scalar!

macro_rules! query_scalar {
    ($sql:expr $(, $args:expr)* $(,)?) => { ... }
}
Compile-time verified equivalent of sqlx::query_scalar. Extracts the first column of each row into the inferred Rust scalar type.
let count: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM users")
    .fetch_one(&pool)
    .await?;

let username: Option<String> = sqlx::query_scalar!(
    "SELECT username FROM users WHERE id = $1",
    id
)
.fetch_optional(&pool)
.await?;

Type override syntax (detailed)

All three query macros support the same type override syntax. Use it to:
  • Resolve ambiguity when both chrono and time are enabled (also configurable via sqlx.toml)
  • Pass slice references (arrays) to Postgres ANY($1) clauses
  • Use custom newtype wrappers
// Force the column to decode as chrono's DateTime instead of time's
let created_at = sqlx::query_scalar!(
    "SELECT created_at FROM events WHERE id = $1",
    id as i64,
)
// Override the output type
// .map(...) or use query_as! for named struct output
The pattern &expr as &Type is the most common form:
sqlx::query!(
    "INSERT INTO tags(name) SELECT * FROM UNNEST($1::text[])",
    &tag_names as &[String],
)
.execute(&pool)
.await?;

migrate!

macro_rules! migrate {
    () => { ... };
    ($dir:literal) => { ... };
}
Embeds your migration files into the binary at compile time and returns a Migrator that can apply them at runtime. Requires the migrate feature in addition to macros.
[dependencies]
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "macros", "migrate"] }
By default, migrate!() looks for migration files in a migrations/ directory relative to the crate root. Pass a string literal to use a different path.
// Uses ./migrations relative to crate root
sqlx::migrate!().run(&pool).await?;

// Custom directory
sqlx::migrate!("db/migrations").run(&pool).await?;
Migration files follow the naming convention <VERSION>_<description>.sql for simple (non-reversible) migrations, or <VERSION>_<description>.up.sql / <VERSION>_<description>.down.sql for reversible ones. The migration table defaults to _sqlx_migrations and can be renamed in sqlx.toml.

#[sqlx::test]

#[sqlx::test]
A test attribute macro that automatically provisions a fresh, isolated database for each test function and tears it down afterwards. This eliminates shared state between tests and removes the need for manual transaction rollbacks or table truncation. Requires both macros and migrate features.
[dependencies]
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "macros", "migrate"] }

Basic usage

Annotate an async test function with #[sqlx::test]. SQLx injects a PgPool (or the pool type matching your enabled database feature) that is pre-configured for isolation.
#[sqlx::test]
async fn test_create_user(pool: PgPool) -> sqlx::Result<()> {
    sqlx::query!("INSERT INTO users(name) VALUES ($1)", "Alice")
        .execute(&pool)
        .await?;

    let count: i64 = sqlx::query_scalar!("SELECT COUNT(*) FROM users")
        .fetch_one(&pool)
        .await?;

    assert_eq!(count, 1);
    Ok(())
}

Automatic migrations

If your project uses migrate!(), passing a Migrator to the attribute runs migrations before each test:
#[sqlx::test(migrator = "MIGRATOR")]
async fn test_with_schema(pool: PgPool) -> sqlx::Result<()> {
    // `pool` points to a database that already has all migrations applied
    Ok(())
}

static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations");

What it does under the hood

  1. Creates a new database by cloning a template (Postgres) or using an in-memory database (SQLite).
  2. Optionally applies migrations to bring the schema up to date.
  3. Runs the test body with a pool connected to that database.
  4. Drops the database when the test completes (pass or fail).
#[sqlx::test] integrates with the standard cargo test runner and requires no external test harness. Set DATABASE_URL to a superuser connection URL for Postgres so SQLx can create and drop test databases.

Build docs developers (and LLMs) love