Skip to content
EworksskPublic

About

Laravel Eloquent-style ORM for Rust

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Rustique: Eloquent-style ORM for Rust

Rust Edition MySQL Oracle Async unsafe

Active-record models, a fluent query builder, relations, migrations and factories. One API, two SQL dialects.

Documentation · Getting started · Cheat sheet


If you know Laravel Eloquent, you already know this library. Rustique brings the same model-centric workflow to Rust, with plain structs, compile-time column constants and no runtime magic.

use rustique::prelude::*;

#[rustique::model(table = "users", timestamps, soft_deletes)]
#[derive(Debug, Clone, Default)]
pub struct User {
    pub id: i64,
    pub name: String,
    pub email: String,
    pub created_at: Option<NaiveDateTime>,
    pub updated_at: Option<NaiveDateTime>,
    pub deleted_at: Option<NaiveDateTime>,
    #[rustique(has_many(Post))]
    pub posts: Rel<Vec<Post>>,
}

let db = Db::connect("mysql://user:pass@localhost/app").await?;

// Query, filter, eager load
let users = User::query()
    .where_(User::EMAIL, "like", "%@example.com")
    .order_by_desc(User::ID)
    .with("posts.comments")          // one query per level, no N+1
    .get(&db).await?;

// Create, update (dirty columns only), soft delete
let mut user = User::create(&db, User { name: "Ann".into(), ..Default::default() }).await?;
user.name = "Anne".into();
user.save(&db).await?;
user.delete(&db).await?;

// Transactions roll back on error or drop
db.transaction(async |tx| {
    user.save(tx).await?;
    order.save(tx).await?;
    Ok::<_, rustique::Error>(order.id)
}).await?;

Highlights

Active-record models #[rustique::model] derives the whole thing. Dirty tracking, timestamps, soft deletes, UUID / ULID keys, mass assignment.
Fluent query builder Wheres (dates, JSON, subqueries), joins, unions, aggregates, upsert, bulk insert. Every column gets a constant, so typos fail to compile.
Relationships has_one, has_many, belongs_to, belongs_to_many, through, polymorphic and morph_to_many. Eager loading, with_count, where_has, pivots.
Two databases, one API MySQL 8.4+ (pure Rust driver, rustls) and Oracle 19c+. The grammar handles quoting, limits, identity columns and JSON for you.
Migrations and schema builder Schema::create, Blueprint, a Migrator with fresh / reset / --step, seeders and a ready-made CLI.
Scopes, hooks, observers Local and global scopes, lifecycle hooks (creating, saved, deleting, ...) and observers.
Large results paginate, simple_paginate, cursor_paginate, chunk, chunk_by_id and streaming cursors.
Factories User::factory().count(10).create(&db) for tests and seeders.
Casts #[derive(rustique::Cast)] maps JSON and custom types to columns.
Safe by default unsafe is forbidden. N+1 detection (prevent_lazy_loading), deadlock retries, savepoints, query logging through tracing.

Install

Published on crates.io as rustique-orm; the library is imported as rustique:

[dependencies]
rustique-orm = "0.3"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
chrono = "0.4"

MySQL only? Skip the Oracle client entirely:

rustique-orm = { version = "0.3", default-features = false, features = ["mysql", "mysql-tls"] }
Feature Default What it does
mysql yes MySQL driver (mysql_async, pure Rust)
mysql-tls yes TLS through rustls (ring provider; mysql-tls-aws-lc-rs for aws-lc-rs)
oracle yes Oracle driver. Needs Oracle Instant Client at runtime only
schema-check no Checks model structs against a schema snapshot at compile time

Requires Rust 1.88 or newer.

More taste

Relations and constrained eager loading

let users = User::query()
    .with_where("posts", |q| q.where_("published", "=", true))
    .with("posts:id,title")
    .get(&db).await?;

let drafts = user.posts().where_("published", "=", false).get(&db).await?;

Migrations

Schema::create(&db, "users", |t| {
    t.id();
    t.string("email", 255).unique();
    t.boolean("active").default(true);
    t.foreign_id("country_id").constrained("countries").cascade_on_delete();
    t.json("settings").nullable();
    t.timestamps();
    t.soft_deletes();
}).await?;

Pagination without the offset cost

let page = User::query().cursor_paginate(&db, 50, None).await?;
// page.next_cursor for the next one

Factories

impl HasFactory for User {
    fn definition(n: u64) -> Self {
        User { name: format!("User {n}"), email: format!("user{n}@example.com"), ..Default::default() }
    }
}

Benchmarks

Mean time per operation against diesel and Laravel Eloquent on the same data (100,000 users, 1M posts). Bars are scaled per scenario, so compare within a group.

Benchmark chart: rustique, diesel and Eloquent on MySQL and Oracle

All scenarios, percentiles and memory: MySQL · Oracle · how it is measured.

Documentation

The pages follow the Laravel Eloquent docs layout. Every code block compiles as a doctest.

Getting started Install, features, connecting, pool and TLS
Query builder Selects, wheres, joins, aggregates, writes
Models Retrieving, saving, timestamps, soft deletes, dirty tracking
Relationships All relation kinds, eager loading, pivots
Casts Cast, FromValue, custom types
Scopes, hooks and observers Reusable filters and lifecycle events
Pagination, chunking and cursors Working with big tables
Transactions and raw SQL Replicas, nested transactions, retries, logging
Factories Test data and seeding
Schema and migrations Schema builder, Migrator, CLI
Database differences What changes between MySQL and Oracle
Eloquent compatibility What maps to Laravel, what does not

Development

Run the database-free tests (they check generated SQL for both dialects):

cargo test --workspace --all-features

Integration tests need real databases. Start them and point the tests at them:

docker compose up -d
export RUSTIQUE_TEST_MYSQL_URL=mysql://orm:secret@127.0.0.1:13306/orm_test
export RUSTIQUE_TEST_ORACLE_URL=oracle://orm:secret@127.0.0.1:11521/XEPDB1
cargo test --workspace --all-features

Before sending a change:

cargo fmt --all
cargo clippy --workspace --all-targets --all-features

Layout: rustique/ is the library, rustique-macros/ the proc macros, rustique/docs/ the user guide, and bench/ the benchmark against diesel and Eloquent.

Status

Pre-release. The API covers the full model, relation, migration and query surface described above and is tested against MySQL 8.4 and Oracle XE 21c.

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

Laravel Eloquent-style ORM for Rust

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages