Database Libraries

Libraries for database connectivity and ORM in Rust.

sqlx

Async, compile-time checked SQL queries.

[dependencies]
sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }
tokio = { version = "1", features = ["full"] }

Connection

use sqlx::postgres::PgPoolOptions;

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

    Ok(())
}

Compile-Time Checked Queries

use sqlx::FromRow;

#[derive(FromRow)]
struct User {
    id: i32,
    name: String,
    email: String,
}

async fn get_user(pool: &PgPool, id: i32) -> Result<User, sqlx::Error> {
    sqlx::query_as!(User, "SELECT id, name, email FROM users WHERE id = $1", id)
        .fetch_one(pool)
        .await
}

Runtime Queries

use sqlx::{Row, FromRow};

async fn dynamic_query(pool: &PgPool) -> Result<Vec<User>, sqlx::Error> {
    let users = sqlx::query_as::<_, User>("SELECT * FROM users")
        .fetch_all(pool)
        .await?;

    Ok(users)
}

async fn get_scalar(pool: &PgPool) -> Result<i64, sqlx::Error> {
    let count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
        .fetch_one(pool)
        .await?;

    Ok(count.0)
}

Transactions

async fn transfer(pool: &PgPool, from: i32, to: i32, amount: i64) -> Result<(), sqlx::Error> {
    let mut tx = pool.begin().await?;

    sqlx::query("UPDATE accounts SET balance = balance - $1 WHERE id = $2")
        .bind(amount)
        .bind(from)
        .execute(&mut *tx)
        .await?;

    sqlx::query("UPDATE accounts SET balance = balance + $1 WHERE id = $2")
        .bind(amount)
        .bind(to)
        .execute(&mut *tx)
        .await?;

    tx.commit().await?;
    Ok(())
}

Migrations

# Install CLI
cargo install sqlx-cli

# Create migration
sqlx migrate add create_users

# Run migrations
sqlx migrate run
// Run migrations in code
sqlx::migrate!("./migrations")
    .run(&pool)
    .await?;

diesel

Synchronous ORM with type-safe query builder.

[dependencies]
diesel = { version = "2", features = ["postgres"] }
dotenvy = "0.15"

Schema

// schema.rs (generated by diesel)
diesel::table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        email -> Varchar,
    }
}

Models

use diesel::prelude::*;

#[derive(Queryable, Selectable)]
#[diesel(table_name = crate::schema::users)]
struct User {
    id: i32,
    name: String,
    email: String,
}

#[derive(Insertable)]
#[diesel(table_name = crate::schema::users)]
struct NewUser<'a> {
    name: &'a str,
    email: &'a str,
}

Queries

use diesel::prelude::*;

fn get_users(conn: &mut PgConnection) -> QueryResult<Vec<User>> {
    use crate::schema::users::dsl::*;

    users.load(conn)
}

fn get_user_by_id(conn: &mut PgConnection, user_id: i32) -> QueryResult<User> {
    use crate::schema::users::dsl::*;

    users.find(user_id).first(conn)
}

fn create_user(conn: &mut PgConnection, new_user: NewUser) -> QueryResult<User> {
    use crate::schema::users;

    diesel::insert_into(users::table)
        .values(&new_user)
        .returning(User::as_returning())
        .get_result(conn)
}

sea-orm

Async ORM inspired by ActiveRecord.

[dependencies]
sea-orm = { version = "0.12", features = ["runtime-tokio-native-tls", "sqlx-postgres"] }

Entity

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "users")]
pub struct Model {
    #[sea_orm(primary_key)]
    pub id: i32,
    pub name: String,
    pub email: String,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}

impl ActiveModelBehavior for ActiveModel {}

Queries

use sea_orm::*;

async fn get_users(db: &DatabaseConnection) -> Result<Vec<user::Model>, DbErr> {
    User::find().all(db).await
}

async fn get_user(db: &DatabaseConnection, id: i32) -> Result<Option<user::Model>, DbErr> {
    User::find_by_id(id).one(db).await
}

async fn create_user(db: &DatabaseConnection, name: &str, email: &str) -> Result<user::Model, DbErr> {
    let new_user = user::ActiveModel {
        name: Set(name.to_owned()),
        email: Set(email.to_owned()),
        ..Default::default()
    };

    new_user.insert(db).await
}

rusqlite

SQLite bindings for Rust.

[dependencies]
rusqlite = { version = "0.31", features = ["bundled"] }
use rusqlite::{Connection, Result};

fn main() -> Result<()> {
    let conn = Connection::open("my_db.db")?;

    conn.execute(
        "CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY,
            name TEXT NOT NULL,
            email TEXT NOT NULL
        )",
        [],
    )?;

    conn.execute(
        "INSERT INTO users (name, email) VALUES (?1, ?2)",
        ["Alice", "alice@example.com"],
    )?;

    let mut stmt = conn.prepare("SELECT id, name, email FROM users")?;
    let users = stmt.query_map([], |row| {
        Ok(User {
            id: row.get(0)?,
            name: row.get(1)?,
            email: row.get(2)?,
        })
    })?;

    for user in users {
        println!("{:?}", user?);
    }

    Ok(())
}

redis

Redis client for Rust.

[dependencies]
redis = { version = "0.25", features = ["tokio-comp"] }
use redis::AsyncCommands;

#[tokio::main]
async fn main() -> redis::RedisResult<()> {
    let client = redis::Client::open("redis://127.0.0.1/")?;
    let mut con = client.get_multiplexed_async_connection().await?;

    // Set a value
    con.set("key", "value").await?;

    // Get a value
    let value: String = con.get("key").await?;

    // Set with expiry
    con.set_ex("temp_key", "temp_value", 60).await?;

    // Hash operations
    con.hset("user:1", "name", "Alice").await?;
    let name: String = con.hget("user:1", "name").await?;

    Ok(())
}

Comparison

Crate Type Async Compile-Time Check
sqlx Raw SQL Yes Yes
diesel ORM No Yes
sea-orm ORM Yes No
rusqlite Raw SQL No No

Connection Pooling

sqlx Pool

let pool = PgPoolOptions::new()
    .max_connections(10)
    .min_connections(2)
    .acquire_timeout(Duration::from_secs(30))
    .connect(&database_url)
    .await?;

diesel + r2d2

use diesel::r2d2::{ConnectionManager, Pool};

let manager = ConnectionManager::<PgConnection>::new(database_url);
let pool = Pool::builder()
    .max_size(10)
    .build(manager)?;

let conn = pool.get()?;

Summary

Crate Use Case
sqlx Async SQL with compile-time checks
diesel Type-safe ORM
sea-orm Async ActiveRecord-style ORM
rusqlite SQLite
redis Redis client

Choosing a Database Library

Need Recommendation
Compile-time SQL checking sqlx
Full ORM with sync diesel
Full ORM with async sea-orm
SQLite embedded rusqlite
Key-value cache redis

Back to top

Rust Programming Guide is not affiliated with the Rust Foundation. Content is provided for educational purposes.

This site uses Just the Docs, a documentation theme for Jekyll.