rok-fluent 0.4.1

Eloquent-inspired async ORM for Rust (PostgreSQL, MySQL, SQLite)
docs.rs failed to build rok-fluent-0.4.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: rok-fluent-0.4.0

rok-fluent

Eloquent-inspired async ORM for Rust — PostgreSQL, MySQL, SQLite via SQLx.

[dependencies]
rok-fluent = { version = "0.4", features = ["active", "query", "postgres"] }

Features

Flag What it enables
macros (default) #[derive(Model, Table, Resource, Seed)], query! macro
active Active Record — ModelQuery, PgModel, CrudService, scopes, eager loading
query Typed DSL — db::select().from(table).where_(…), JOINs, CTEs, aggregates, window functions
postgres / sqlite / mysql Database backend
axum OrmLayer middleware (implies postgres)
tracing / metrics OpenTelemetry spans / Prometheus counters
tenant Multi-tenancy via Tower layer
replica Read-replica routing strategies
factory / factory-postgres Test factories with Faker data generator
migrate / migrate-postgres / migrate-sqlite / migrate-mysql Schema migration runner
cli rok db CLI — migrate, rollback, status, schema dump, schema diff
full Everything above

Quick Start

Typed DSL (query feature)

use rok_fluent::dsl::db;

#[derive(Debug, sqlx::FromRow, rok_fluent::Table)]
#[table(name = "users")]
pub struct User {
    pub id:    i64,
    pub name:  String,
    pub email: String,
}

// SELECT … FROM users WHERE email LIKE $1 ORDER BY name ASC LIMIT 25
let users: Vec<User> = db::select()
    .from(User::table())
    .where_(User::EMAIL.like("%@example.com"))
    .order_by(User::NAME.asc())
    .limit(25)
    .fetch_all(&pool).await?;

// INSERT … RETURNING *
let user: User = db::insert_into(User::table())
    .values([("name", "Alice"), ("email", "alice@example.com")])
    .returning()
    .fetch_one(&pool).await?;

// Upsert — INSERT … ON CONFLICT DO UPDATE
let user: User = db::insert_into(User::table())
    .values_typed([(User::EMAIL, "alice@example.com"), (User::NAME, "Alice")])
    .on_conflict(User::EMAIL).do_update([(User::NAME, "Alice")])
    .returning()
    .fetch_one(&pool).await?;

// Join with typed ON clause
let rows: Vec<(User, Post)> = db::select()
    .from(User::table())
    .inner_join(Post::table(), Post::USER_ID.references(User::ID))
    .fetch_all(&pool).await?;

// Pagination
let page: Page<User> = db::select()
    .from(User::table())
    .paginate(1, 25, &pool).await?;

// Window functions — rank, row_number, lag, lead
use rok_fluent::dsl::{rank, dense_rank, Window, row_number};

let ranked: Vec<(User, Option<i64>)> = db::select()
    .from(User::table())
    .win_col(rank().over(Window::new().order_by(User::SCORE.desc())).alias("rank"))
    .fetch_all(&pool).await?;

Active Record (active + postgres features)

use rok_fluent::Model;

#[derive(Debug, sqlx::FromRow, Model)]
#[model(table = "users", timestamps)]
pub struct User {
    pub id:    i64,
    pub name:  String,
    pub email: String,
    pub active: bool,
}

// Fluent query
let users = User::query()
    .where_eq("active", true)
    .order_by_desc("created_at")
    .limit(20)
    .all().await?;

// Find by primary key
let user = User::find(42_i64).await?;

// Paginate
let page: Page<User> = User::query()
    .where_eq("active", true)
    .paginate(1, 25).await?;

Transactions & Locking

use rok_fluent::services::{TransactionService, LockService};

// Transaction with savepoints
let tx = TransactionService::begin(&pool).await?;
let result = tx.create(&[("name", "Alice".into())]).await?;
tx.commit().await?;

// Advisory lock
LockService::acquire("deploy_lock", &pool).await?;
// ... critical section ...
LockService::release("deploy_lock", &pool).await?;

Service Layer (active + postgres)

use rok_fluent::services::{CrudService, FilterBuilder, SortBuilder, BatchService};

let crud = CrudService::<User>::new(pool.clone());

// CRUD
let user = crud.find(42).await?;
let page = crud.paginate(1, 25).await?;
let created = crud.create(&[("name", "Alice".into()), ("email", "a@b.com".into())]).await?;
let updated = crud.update(42, &[("name", "Bob".into())]).await?;
crud.delete(42).await?;

// Filtering & sorting
let results = crud.query()
    .apply(FilterBuilder::default().eq("active", true))
    .apply(SortBuilder::default().allow("name", "created_at").then_by("name", "asc"))
    .paginate(1, 25).await?;

// Batch operations
let batch = BatchService::<User>::new(pool.clone());
let ids = batch.bulk_insert(&[user1, user2, user3]).await?;
batch.bulk_upsert_by("email", &[user1, user2]).await?;

Migration from rok-orm 0.3

# Before (5 crates)
rok-orm         = { version = "0.3", features = ["postgres", "macros"] }
rok-orm-core    = { version = "0.3" }
rok-orm-macros  = { version = "0.3" }
rok-orm-migrate = { version = "0.3", features = ["postgres"] }
rok-orm-factory = { version = "0.3", features = ["postgres"] }

# After (1 crate)
rok-fluent = { version = "0.4", features = ["postgres", "macros", "migrate-postgres", "factory-postgres"] }

Import path changes:

  • rok_orm::rok_fluent::
  • rok_orm_core::SqlValuerok_fluent::SqlValue
  • rok_orm_migrate::MigrationRunnerrok_fluent::migrate::MigrationRunner
  • #[derive(rok_orm_macros::Model)]#[derive(rok_fluent::Model)]

Resources

License

MIT