TL;DR: SQLx is Rust's leading async SQL toolkit in 2026 and the best fit for teams that want real SQL without ORM indirection. Its key feature is compile-time query checking through
query!andquery_as!, which catches broken SQL before the code ships. Choose SQLx if you are building async Rust backends with PostgreSQL, MySQL, or SQLite and you want strong type safety without giving up direct control over queries.
What Is SQLx?
SQLx is an async, compile-time-checked SQL library for Rust that gives you raw SQL, strong type safety, and no ORM abstraction layer in the middle.
This page matters most for backend engineers, API builders, and teams deciding between SQLx and Diesel for a real Rust service.
# Cargo.toml
[dependencies]
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "macros"] }
tokio = { version = "1", features = ["full"] }use sqlx::PgPool;
#[derive(sqlx::FromRow, Debug)]
struct User {
id: i64,
name: String,
email: String,
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = PgPool::connect("postgres://localhost/mydb").await?;
// query_as!; compile-time verified, results mapped to User
let users = sqlx::query_as!(User, "SELECT id, name, email FROM users")
.fetch_all(&pool)
.await?;
for user in users {
println!("{:?}", user);
}
Ok(())
}How Does SQLx Work?
SQLx works by combining async database drivers, typed row mapping, and compile-time query verification against a real database schema or cached metadata.
// If `usres` doesn't exist, SQLx fails at compile time
let _ = sqlx::query!("SELECT * FROM usres").fetch_all(&pool).await?;
// error: relation "usres" does not exist
// If email is nullable in the DB, SQLx catches the mismatch
let users = sqlx::query_as!(User, "SELECT id, name, email FROM users");
// error: expected `String`, found `Option<String>`; email is nullable in DBTo enable offline checking in CI, run cargo sqlx prepare so the query metadata is cached and the build does not need a live database connection every time.
When Should You Use SQLx?
Use SQLx when you want async Rust database access with real SQL, strong compile-time guarantees, and clean integration with Tokio-first backend stacks.
SQLx is a strong fit when:
- you are building an async API or service with Axum, Actix, or Tokio
- your team prefers writing explicit SQL instead of learning a query DSL
- you want compile-time validation of queries and result shapes
- you need PostgreSQL, MySQL, or SQLite support without switching libraries
SQLx is a weaker fit when your team wants a heavier ORM-style abstraction or when offline schema-first workflows matter more than direct SQL ergonomics.
How Do You Run Queries With SQLx?
SQLx gives you different query macros and fetch methods depending on whether you want rows, one value, or side-effecting writes.
use sqlx::PgPool;
async fn examples(pool: &PgPool) -> Result<(), sqlx::Error> {
// Fetch all rows
let users = sqlx::query_as!(User, "SELECT id, name, email FROM users")
.fetch_all(pool)
.await?;
// Fetch one row; error if zero or more than one
let user = sqlx::query_as!(User, "SELECT id, name, email FROM users WHERE id = $1", 1i64)
.fetch_one(pool)
.await?;
// Fetch optional; None if not found
let maybe_user = sqlx::query_as!(User,
"SELECT id, name, email FROM users WHERE email = $1",
"[email protected]"
)
.fetch_optional(pool)
.await?;
// Scalar value
let count = sqlx::query_scalar!("SELECT COUNT(*) FROM users")
.fetch_one(pool)
.await?;
// Execute (INSERT/UPDATE/DELETE)
sqlx::query!("DELETE FROM users WHERE id = $1", 42i64)
.execute(pool)
.await?;
Ok(())
}How Do You Manage Database Migrations With SQLx?
SQLx includes a built-in migration system where SQL files are applied in order and tracked in a _sqlx_migrations table.
# Install the CLI
cargo install sqlx-cli
# Create a new migration
sqlx migrate add create_users_table
# Apply pending migrations
sqlx migrate run
# Revert last migration
sqlx migrate revert-- migrations/20260408000001_create_users_table.sql
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
);// Apply migrations at startup
sqlx::migrate!("./migrations").run(&pool).await?;SQLx vs Diesel in 2026
Choose SQLx for most async Rust backends in 2026, and choose Diesel mainly when a schema-driven query builder fits your team better than raw SQL.
| SQLx | Diesel | |
|---|---|---|
| SQL style | Raw SQL | Query builder DSL |
| Async | ✅ Native | ❌ (sync; diesel-async exists) |
| Compile-time checks | ✅ Against live DB or prepared metadata | ✅ Against schema |
| Learning curve | Low (know SQL? you know SQLx) | Higher (learn Diesel DSL) |
| Flexibility | High (write any SQL) | Medium (complex queries get awkward) |
| Migrations | Built-in | Built-in |
| Best fit | Async web backends | Schema-driven Rust data layers |
If your team already thinks in SQL and is building async services, SQLx is usually the better choice. If your team wants a more guided query-builder style and is comfortable with Diesel's DSL, Diesel can still be the right pick.
Why Does SQLx Matter Professionally?
SQLx matters because it lets Rust backend engineers keep full control over SQL while still getting compile-time safety that catches expensive production mistakes early.
That is a meaningful career skill. Many teams want engineers who can write real SQL, reason about query performance, and still stay inside a modern async Rust stack. SQLx sits directly in that overlap. It is one of the clearest examples of Rust offering stronger guarantees without forcing an ORM-first workflow.
Frequently Asked Questions
Not if you use offline mode. Run cargo sqlx prepare once with the database running, it caches query metadata in sqlx-data.json. Commit this file; CI can then compile without a live database.
query! returns an anonymous record type, you access columns by name but can't store the type easily. query_as! maps to a named struct that implements FromRow, much more ergonomic for function return types and collections.
let mut tx = pool.begin().await?;
sqlx::query!("INSERT INTO users (name) VALUES ($1)", "Alice").execute(&mut *tx).await?;
sqlx::query!("INSERT INTO logs (msg) VALUES ($1)", "created user").execute(&mut *tx).await?;
tx.commit().await?;Yes. SQLx is production-ready and widely used in Rust backend work. The main operational tradeoff is compile-time schema checking, which teams usually handle with cargo sqlx prepare in CI/CD.
Sources
Related Glossary Terms
- Async/Await: SQLx is fully async and requires a Tokio runtime
- Tokio: The runtime SQLx uses for async I/O
- Result: All SQLx operations return
Result<T, sqlx::Error> - Axum: Axum + SQLx is the most common Rust web + database stack
- SeaORM: SeaORM sits one layer above SQLx-style async database access
- Chrono: SQLx commonly maps database timestamps into Chrono date-time types
