Drizzle RS
A type-safe SQL query builder / ORM-ish layer for Rust, inspired by Drizzle ORM.
[!WARNING]
This project is still evolving. Expect breaking changes.
Install
Library
Pick a database driver feature (drivers imply the corresponding dialect module):
[dependencies]
drizzle = { version = "0.1.4", features = ["rusqlite"] }
CLI
Install the drizzle binary:
cargo install drizzle-cli --locked --all-features
Define your schema (schema.rs)
Convention: keep all table definitions in a dedicated schema.rs module. Each #[SQLiteTable] generates Select*, Insert*, and Update* companion types.
use drizzle::sqlite::prelude::*;
#[SQLiteTable]
pub struct Users {
#[column(primary, autoincrement)]
pub id: i64,
pub name: String,
pub email: Option<String>,
pub age: i64,
}
#[SQLiteTable]
pub struct Posts {
#[column(primary, autoincrement)]
pub id: i64,
pub title: String,
pub content: Option<String>,
#[column(references = Users::id)]
pub author_id: i64,
}
#[derive(SQLiteSchema)]
pub struct Schema {
pub users: Users,
pub posts: Posts,
}
See examples/rusqlite.rs for a full runnable example.
Connect
use drizzle::sqlite::rusqlite::Drizzle;
let conn = rusqlite::Connection::open("app.db")?;
let (db, Schema { users, posts }) = Drizzle::new(conn, Schema::new());
db.create()?;
CRUD
Insert
db.insert(users)
.values([
InsertUsers::new("Alex Smith", 26i64).with_email("alex@example.com"),
InsertUsers::new("Jordan Lee", 30i64),
])
.execute()?;
Select
use drizzle::core::expr::eq;
let all: Vec<SelectUsers> = db.select(()).from(users).all()?;
let user: SelectUsers = db
.select(())
.from(users)
.r#where(eq(users.name, "Alex Smith"))
.get()?;
let names: Vec<(String,)> = db
.select((users.name,))
.from(users)
.all()?;
Update
use drizzle::core::expr::eq;
db.update(users)
.set(UpdateUsers::default().with_age(27))
.r#where(eq(users.id, 1))
.execute()?;
Delete
use drizzle::core::expr::eq;
db.delete(users)
.r#where(eq(users.id, 1))
.execute()?;
Joins
Use #[derive(SQLiteFromRow)] to map columns from multiple tables into a flat struct:
use drizzle::core::expr::eq;
use drizzle::sqlite::prelude::*;
#[derive(SQLiteFromRow, Default, Debug)]
struct UserWithPost {
#[column(Users::id)]
user_id: i64,
#[column(Users::name)]
name: String,
#[column(Posts::id)]
post_id: i64,
#[column(Posts::content)]
content: Option<String>,
}
let rows: Vec<UserWithPost> = db
.select(UserWithPost::default())
.from(users)
.left_join(posts, eq(users.id, posts.author_id))
.all()?;
PostgreSQL
The same patterns apply — swap the macro and driver:
use drizzle::postgres::prelude::*;
use drizzle::postgres::sync::Drizzle;
#[PostgresTable]
struct Accounts {
#[column(serial, primary)]
id: i32,
name: String,
}
#[derive(PostgresSchema)]
struct Schema {
accounts: Accounts,
}
fn main() -> drizzle::Result<()> {
let client = postgres::Client::connect(
"host=localhost user=postgres dbname=drizzle_test",
postgres::NoTls,
)?;
let (mut db, Schema { accounts }) = Drizzle::new(client, Schema::new());
db.create()?;
db.insert(accounts)
.values([InsertAccounts::new("Acme")])
.execute()?;
let rows: Vec<SelectAccounts> = db.select(()).from(accounts).all()?;
println!("accounts: {rows:?}");
Ok(())
}
For async, use drizzle::postgres::tokio::Drizzle with tokio_postgres::connect.
CLI (migrations)
drizzle init -d sqlite drizzle generate drizzle migrate
License
MIT — see LICENSE.