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.

This guide walks you through adding SQLx to an existing Rust project, creating a connection pool, and running both runtime and compile-time verified queries against a PostgreSQL database. The same patterns apply to MySQL, MariaDB, and SQLite — differences are called out inline.
1

Add SQLx to Cargo.toml

Add SQLx as a dependency with your chosen runtime, TLS backend, and database driver. The following example uses Tokio and PostgreSQL:
Cargo.toml
[dependencies]
sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "postgres", "macros"] }
tokio = { version = "1", features = ["full"] }
Adjust the features to match your stack:
[dependencies]
sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "mysql", "macros"] }
tokio = { version = "1", features = ["full"] }
You must choose exactly one runtime feature (runtime-tokio, runtime-async-std, etc.) and, if connecting over TLS, one TLS feature (tls-native-tls or one of the tls-rustls-* variants). See Installation and feature flags for the full reference.
2

Set the DATABASE_URL environment variable

SQLx reads DATABASE_URL at both compile time (for the query! macro) and runtime (for your application code). Set it to a connection string for your database:
export DATABASE_URL="postgres://postgres:password@localhost/mydb"
For convenience, you can store this in a .env file at the root of your project. SQLx picks it up automatically via the dotenvy crate:
.env
DATABASE_URL=postgres://postgres:password@localhost/mydb
Connection string formats by database:
DatabaseFormat
PostgreSQLpostgres://user:password@host/database
MySQL / MariaDBmysql://user:password@host/database
SQLite (file)sqlite:./path/to/database.db
SQLite (in-memory)sqlite::memory:
The database you point DATABASE_URL at for compile-time checking does not need to contain data, but it must be the same database kind (PostgreSQL, MySQL, etc.) and have the same schema as the one you will connect to at runtime.
3

Create a connection pool

Most applications should use a connection pool rather than individual connections. Create one using PgPoolOptions and share it across your application:
src/main.rs
use sqlx::postgres::PgPoolOptions;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect("postgres://postgres:password@localhost/mydb")
        .await?;

    // pool is ready to use
    Ok(())
}
In practice, read the connection URL from an environment variable:
src/main.rs
use sqlx::postgres::PgPoolOptions;
use std::env;

#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");

    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect(&database_url)
        .await?;

    Ok(())
}
For other databases, use the corresponding pool type:
use sqlx::mysql::MySqlPool;
let pool = MySqlPool::connect(&database_url).await?;

use sqlx::sqlite::SqlitePool;
let pool = SqlitePool::connect(&database_url).await?;
4

Run a basic query

Use sqlx::query() to run a parameterized query. Parameters use $1, $2, etc. for PostgreSQL, and ? for MySQL/MariaDB and SQLite:
// PostgreSQL uses $1, $2, ...
let row: (i64,) = sqlx::query_as("SELECT $1")
    .bind(150_i64)
    .fetch_one(&pool)
    .await?;

assert_eq!(row.0, 150);
To fetch multiple rows and access columns by name, use fetch_all and row.get():
use sqlx::Row;

let rows = sqlx::query("SELECT id, description FROM todos ORDER BY id")
    .fetch_all(&pool)
    .await?;

for row in rows {
    let id: i64 = row.get("id");
    let description: String = row.get("description");
    println!("{id}: {description}");
}
SQLx always uses prepared statements for sqlx::query() and its variants. Parameters are never interpolated on the client side, which eliminates SQL injection by design.
5

Use the query! macro for compile-time verification

The query! macro verifies your SQL against a live database at compile time. It checks that the SQL is valid for your database and that you are binding the right number and types of parameters. The result columns become fields on an anonymous struct:
// Requires DATABASE_URL to be set at build time
let todos = sqlx::query!(
    r#"
    SELECT id, description, done
    FROM todos
    ORDER BY id
    "#
)
.fetch_all(&pool)
.await?;

for todo in todos {
    println!(
        "- [{}] {}: {}",
        if todo.done { "x" } else { " " },
        todo.id,
        todo.description,
    );
}
Bind parameters are compile-time checked for count and type:
let description = String::from("Buy groceries");

let rec = sqlx::query!(
    r#"
    INSERT INTO todos (description)
    VALUES ($1)
    RETURNING id
    "#,
    description
)
.fetch_one(&pool)
.await?;

println!("Inserted todo with id {}", rec.id);
Add the following to your Cargo.toml to significantly speed up incremental builds when using the query! macro:
Cargo.toml
[profile.dev.package.sqlx-macros]
opt-level = 3
6

Use FromRow to map results to a struct

When you need a named output type, derive sqlx::FromRow on a struct and use query_as! or sqlx::query_as():
use sqlx::FromRow;

#[derive(Debug, FromRow)]
struct Todo {
    id: i64,
    description: String,
    done: bool,
}

// query_as! provides compile-time SQL verification with a named output type
let todos = sqlx::query_as!(
    Todo,
    r#"
    SELECT id, description, done
    FROM todos
    ORDER BY id
    "#
)
.fetch_all(&pool)
.await?;

for todo in &todos {
    println!("{:?}", todo);
}
You can also use sqlx::query_as() (without the macro) when you do not need compile-time SQL verification:
let todos: Vec<Todo> = sqlx::query_as("SELECT id, description, done FROM todos")
    .fetch_all(&pool)
    .await?;
FromRow is available when the derive feature is enabled. The derive feature is automatically enabled by the macros feature.

Next steps

Installation and feature flags

Full reference for every SQLx feature flag, including TLS backends and type integrations.

Introduction

Learn what makes SQLx unique and how compile-time query verification works under the hood.

Build docs developers (and LLMs) love