| Crates.io | sqlx-utils |
| lib.rs | sqlx-utils |
| version | 1.1.3 |
| created_at | 2025-03-08 15:27:08.346335+00 |
| updated_at | 2025-03-09 23:11:28.060307+00 |
| description | Utilities for working with SQLx in a structured and efficient way, both when developing and running |
| homepage | |
| repository | https://github.com/retrokiller543/sqlx_utils |
| max_upload_size | |
| id | 1584600 |
| size | 252,581 |
SQLx Utils provides a comprehensive set of utilities for working with the SQLx library in a structured and efficient way. It simplifies database interactions through type-safe filters, repository patterns, and powerful batch operations.
Add SQLx Utils to your Cargo.toml:
[dependencies]
sqlx-utils = "1.1.1"
By default, the crate enables the any database feature. To use a specific database:
[dependencies]
sqlx-utils = { version = "1.1.0", default-features = false, features = ["postgres"] }
Available database features:
any (default): Works with any SQLx-supported databasepostgres: PostgreSQL specificmysql: MySQL specificsqlite: SQLite specificuse sqlx_utils::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// For any DB (with the `any` feature)
install_default_drivers();
// Initialize the pool
let pool = PoolOptions::new()
.max_connections(5)
.connect("your_connection_string").await?;
initialize_db_pool(pool);
Ok(())
}
use sqlx_utils::prelude::*;
sql_filter! {
pub struct UserFilter {
SELECT * FROM users WHERE
?id = i64 AND
?name LIKE String AND
?age > i32
}
}
// Usage:
let filter = UserFilter::new()
.id(42)
.name("Alice%");
use sqlx_utils::prelude::*;
// 1. Define your model
struct User {
id: i64,
name: String,
email: String,
}
impl Model for User {
type Id = i64;
fn get_id(&self) -> Option<Self::Id> {
Some(self.id)
}
}
// 2. Create a repository
repository! {
pub UserRepo<User>;
}
// 3. Implement operations
repository_insert! {
UserRepo<User>;
insert_query(user) {
sqlx::query("INSERT INTO users (name, email) VALUES (?, ?)")
.bind(&user.name)
.bind(&user.email)
}
}
repository_update! {
UserRepo<User>;
update_query(user) {
sqlx::query("UPDATE users SET name = ?, email = ? WHERE id = ?")
.bind(&user.name)
.bind(&user.email)
.bind(user.id)
}
}
repository_delete! {
UserRepo<User>;
delete_by_id_query(id) {
sqlx::query("DELETE FROM users WHERE id = ?")
.bind(id)
}
}
use sqlx_utils::prelude::*;
#[tokio::main]
async fn main() -> Result<(), sqlx_utils::Error> {
// Create a new user
let user = User {
id: 0, // Will be assigned by DB
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
};
// Insert the user
let user = USER_REPO.insert(user).await?;
// Update the user
let user = User {
id: user.id,
name: "Alice Smith".to_string(),
email: user.email,
};
USER_REPO.update(user).await?;
// Delete the user
USER_REPO.delete_by_id(user.id).await?;
Ok(())
}
async fn transfer_funds(from: i64, to: i64, amount: f64) -> Result<(), sqlx_utils::Error> {
ACCOUNT_REPO.with_transaction(|mut tx| async move {
// Deduct from source account
let from_account = ACCOUNT_REPO.get_by_id_with_executor(&mut tx, from).await?
.ok_or_else(|| Error::Repository { message: "Source account not found".into() })?;
let from_account = Account {
balance: from_account.balance - amount,
..from_account
};
ACCOUNT_REPO.update_with_executor(&mut tx, from_account).await?;
// Add to destination account
let to_account = ACCOUNT_REPO.get_by_id_with_executor(&mut tx, to).await?
.ok_or_else(|| Error::Repository { message: "Destination account not found".into() })?;
let to_account = Account {
balance: to_account.balance + amount,
..to_account
};
ACCOUNT_REPO.update_with_executor(&mut tx, to_account).await?;
(Ok(()), tx)
}).await
}
let admin_filter = UserFilter::new().role("admin");
let active_filter = StatusFilter::new().status("active");
// Combine filters
let active_admins = admin_filter.and(active_filter);
// Process users in batches of 100
let users: Vec<User> = get_many_users();
USER_REPO.insert_batch::<100, _>(users).await?;
impl UserRepo {
pub async fn find_by_email(&self, email: &str) -> Result<Option<User>, Error> {
let filter = UserFilter::new().email(email);
self.get_optional_by_filter(filter).await
}
pub async fn save_with_audit(&self, user: User, actor: &str) -> Result<User, Error> {
self.with_transaction(|mut tx| async move {
let result = self.save_with_executor(&mut tx, user).await?;
// Log audit record
let audit = AuditLog {
entity_type: "user",
entity_id: result.id.to_string(),
actor: actor.to_string(),
action: if result.get_id().is_none() { "create" } else { "update" }.to_string(),
timestamp: chrono::Utc::now(),
};
AUDIT_REPO.insert_with_executor(&mut tx, audit).await?;
(Ok(result), tx)
}).await
}
}
repository! macro creates a static instance using LazyLock, accessible via the uppercase name (e.g., USER_REPO).!zst to the repository macro, you can create repositories with zero runtime cost.filter_debug_impl feature to automatically implement Debug for all generated filters.log_err feature adds error logging to all repository operations.insert_duplicate feature allows inserting records with existing IDs.SQLx Utils provides several repository traits that can be implemented for your models:
SQLx Utils is in active development. The API may evolve between minor versions as I refine the interface based on user feedback.
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.