Expand description
Β§libsql-orm
A powerful, async-first ORM for libsql with first-class support for Cloudflare Workers and WebAssembly environments.
Β§β¨ Features
- π Cloudflare Workers Ready - Built specifically for edge computing environments
- π Async/Await Support - Fully async API with excellent performance
- π― Type-Safe - Leverages Rustβs type system for compile-time safety
- π Rich Query Builder - Fluent API for complex queries
- π Advanced Filtering - Search, pagination, sorting, and aggregations
- π οΈ Migration System - Database schema management and versioning
- π¨ Derive Macros - Automatic model generation with
#[derive(Model)]
- π¦ Bulk Operations - Efficient batch inserts, updates, and deletes
- π WASM Compatible - Optimized for WebAssembly targets
- π Type Conversion - Automatic conversion between SQLite and Rust types
- π Upsert Operations - Smart create_or_update and upsert methods
- π Built-in Logging - Comprehensive logging for debugging and monitoring
Β§π Quick Start
use libsql_orm::{Model, Database};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
#[derive(Model, Debug, Clone, Serialize, Deserialize)]
struct User {
pub id: Option<i64>,
pub name: String,
pub email: String,
pub age: Option<i32>,
pub is_active: bool,
pub created_at: DateTime<Utc>,
}
async fn example() -> Result<(), Box<dyn std::error::Error>> {
// Connect to database
let db = Database::new_connect("libsql://your-db.turso.io", "your-auth-token").await?;
// Create a user
let user = User {
id: None,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
age: Some(30),
is_active: true,
created_at: Utc::now(),
};
// Save to database
let saved_user = user.create(&db).await?;
println!("Created user with ID: [MASKED]");
// Find users
let users = User::find_all(&db).await?;
println!("Found {} users", users.len());
// Use smart upsert operations
let user_with_id = User {
id: Some(123), // Will update if exists, create if not
name: "Updated Name".to_string(),
email: "updated@example.com".to_string(),
age: Some(31),
is_active: true,
created_at: Utc::now(),
};
let smart_saved = user_with_id.create_or_update(&db).await?;
// Upsert by unique constraint
let unique_user = User {
id: None,
name: "Unique User".to_string(),
email: "unique@example.com".to_string(), // Check by email
age: Some(25),
is_active: true,
created_at: Utc::now(),
};
let upserted = unique_user.upsert(&["email"], &db).await?;
Ok(())
}
Β§π Upsert Operations
Smart create-or-update operations for efficient data management:
use libsql_orm::{Model, Database};
// Method 1: create_or_update (based on primary key)
let user = User { id: Some(123), name: "John".to_string(), ... };
let saved = user.create_or_update(&db).await?; // Updates if ID exists, creates if not
// Method 2: upsert (based on unique constraints)
let user = User { id: None, email: "john@example.com".to_string(), ... };
let saved = user.upsert(&["email"], &db).await?; // Updates if email exists, creates if not
// Multiple unique constraints
let saved = user.upsert(&["email", "username"], &db).await?;
Β§π Built-in Logging
Comprehensive logging for debugging and monitoring:
// All database operations are automatically logged
// Logs appear in browser console (WASM) or standard logging (native)
let user = User::new("John", "john@example.com");
// Logs: [INFO] users: Creating record in table: users
// Logs: [DEBUG] users: SQL: INSERT INTO users (...) VALUES (...)
let saved = user.create(&db).await?;
// Logs: [DEBUG] users: Finding record by ID: 123
let found = User::find_by_id(123, &db).await?;
// Logs: [INFO] users: Updating record with ID: 123
let updated = found.unwrap().update(&db).await?;
Β§π Advanced Usage
Β§Custom Table Names and Boolean Type Safety
use libsql_orm::{Model, orm_column, deserialize_bool};
use serde::{Serialize, Deserialize};
#[derive(Model, Debug, Clone, Serialize, Deserialize)]
#[table_name("user_accounts")] // Custom table name
struct User {
#[orm_column(type = "INTEGER PRIMARY KEY AUTOINCREMENT")]
pub id: Option<i64>,
#[orm_column(not_null, unique)]
pub email: String,
pub is_active: bool, // β
Automatic SQLite integer β Rust bool conversion
pub is_verified: bool, // β
Type-safe boolean operations
// For edge cases, use custom deserializer
#[serde(deserialize_with = "deserialize_bool")]
pub has_premium: bool, // β
Manual boolean conversion
#[orm_column(type = "TEXT DEFAULT 'active'")]
pub status: String,
}
// Boolean filtering works seamlessly
let active_users = User::find_where(
FilterOperator::Eq("is_active".to_string(), Value::Boolean(true)),
&db
).await?;
Β§Query Builder
use libsql_orm::{QueryBuilder, FilterOperator, Sort, SortOrder, Pagination};
// Complex query with filtering and pagination
let query = QueryBuilder::new("users")
.select(&["id", "name", "email"])
.r#where(FilterOperator::Gte("age".to_string(), Value::Integer(18)))
.order_by(Sort::new("created_at", SortOrder::Desc))
.limit(10)
.offset(20);
let (sql, params) = query.build()?;
Β§Cloudflare Workers Integration
use worker::*;
use libsql_orm::{Model, Database, MigrationManager, generate_migration};
#[event(fetch)]
async fn fetch(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let database_url = env.var("LIBSQL_DATABASE_URL")?.to_string();
let auth_token = env.var("LIBSQL_AUTH_TOKEN")?.to_string();
let db = Database::new_connect(&database_url, &auth_token).await
.map_err(|e| format!("Database connection failed: {}", e))?;
// Your application logic here
let users = User::find_all(&db).await
.map_err(|e| format!("Query failed: {}", e))?;
Response::from_json(&users)
}
Re-exportsΒ§
pub use database::Database;
pub use error::Error;
pub use error::Result;
pub use filters::Filter;
pub use filters::FilterOperator;
pub use filters::SearchFilter;
pub use filters::Sort;
pub use migrations::templates;
pub use migrations::Migration;
pub use migrations::MigrationBuilder;
pub use migrations::MigrationManager;
pub use model::Model;
pub use pagination::CursorPaginatedResult;
pub use pagination::CursorPagination;
pub use pagination::PaginatedResult;
pub use pagination::Pagination;
pub use query::QueryBuilder;
pub use query::QueryResult;
pub use types::deserialize_bool;
pub use chrono;
pub use types::*;
ModulesΒ§
- database
- Database connection and query execution
- error
- Error handling for libsql-orm
- filters
- Filtering and search functionality for libsql-orm
- macros
- migrations
- Database migration system for libsql-orm
- model
- Model trait and core database operations
- pagination
- Pagination support for libsql-orm
- query
- Query building and execution for libsql-orm
- types
- Type definitions for libsql-orm
MacrosΒ§
- filter
- Helper macro for creating filter conditions
- filter_
op - Helper macro for creating filter operators
- generate_
migration - Re-export the Model macro for convenience Macro to generate migration from a model
- pagination
- Helper macro for creating pagination
- query
- Helper macro for creating query builders
- search
- Helper macro for creating search filters
- sort
- Helper macro for creating sort specifications
StructsΒ§
- Uuid
- A Universally Unique Identifier (UUID).
TraitsΒ§
- Deserialize
- A data structure that can be deserialized from any data format supported by Serde.
- Serialize
- A data structure that can be serialized into any data format supported by Serde.
Attribute MacrosΒ§
- orm_
column - Re-export the Model macro for convenience Column attribute macro for defining SQL column properties
Derive MacrosΒ§
- Deserialize
- Model
- Re-export the Model macro for convenience Derive macro for the Model trait
- Serialize