rainier-orm 1.0.1

Rainier's multi-dialect ORM: one #[derive(Entity)] across SQLite, MySQL, Postgres and D1.
Documentation
rainier-orm-1.0.1 has been yanked.

rainier_orm

A dynamic, multi-dialect DBAL/ORM. You define a plain struct, derive [Entity] on it, and run generic CRUD across SQLite / Cloudflare D1 / MySQL / Postgres — no per-entity repositories written by hand.

Three pieces compose it:

  1. #[derive(Entity)] turns a struct into table + column metadata, a generated row decoder ([Entity::from_row]), and value projections. Column SQL types are deferred to each field type's [SqlType] impl and decoding to its [FromColumn] impl, so adding a supported type is one trait impl — not macro surgery.
  2. SQL generation via sea-query (re-exported), which already renders MySQL / Postgres / SQLite. Dialect translation is therefore free: the same statement is built for whatever the [Executor] reports.
  3. A pluggable [Executor] trait — one impl (sea-orm) covers MySQL, Postgres, and SQLite; the Cloudflare path (D1 over HTTP, D1 native binding in a Worker) implements the same trait. The core is wasm-safe so the Worker can use it; native executors are feature-gated.

Constraints (unique columns, indexes, foreign keys) are derived too; see [schema]. Relationships are not generated as navigation — a foreign key is a flat id field you traverse with [repo::find_by], because the related rows may live in a different backend (a cross-backend JOIN is impossible).

The executors live elsewhere

This crate defines the [Executor] port and implements none of it. Every driver — sea-orm for native MySQL / Postgres / SQLite, Cloudflare D1 over HTTP, libSQL / Turso — lives in rainier-drivers, alongside Redis, Memcached and the AWS clients.

That is not filing for its own sake. It is what keeps this crate wasm-safe with no feature dance: there are no optional dependencies here to accidentally enable, so the core compiles for wasm32 unconditionally and a Worker takes the same rainier-orm a server does.

Prefer a native driver wherever the runtime allows it: sea-orm is faster, supports real transactions, and pools locally. The HTTP executors exist for one reason — a wasm32 runtime has no sockets, so a fetch-based transport is the only way to reach a database there.

use rainier_drivers::sql::SeaOrmExecutor;   // native: preferred
use rainier_drivers::sql::D1Executor;       // wasm: HTTP transport
use rainier_orm::{Entity, repo};

#[derive(Entity)]
#[orm(table = "users")]
struct User {
    #[orm(pk, auto_increment)]
    id: u64,
    #[orm(unique)]
    email: String,
    timezone: Option<String>,
}

# async fn run(db: &impl rainier_orm::Executor) -> rainier_orm::Result<()> {
let id = repo::insert(db, &User { id: 0, email: "a@b.c".into(), timezone: None }).await?;
let u: Option<User> = repo::find_by_pk(db, id).await?;
let by_email: Option<User> = repo::find_one_by(db, "email", "a@b.c").await?;
# Ok(()) }

Beyond CRUD: a fluent query builder ([repo::query] — predicates, joins, first_or_create, partial update / atomic increment), [repo::upsert], a keyset Cursor, derived constraints + DDL ([schema]), and column types past the primitives: Json<T> (serde-as-text), [Base64Bytes] (uniform binary), and string-backed enums via [impl_string_column!].