cratestack-sqlx 0.2.1

Rust-native schema-first framework for typed HTTP APIs, generated clients, and backend services.
Documentation

cratestack-sqlx

SQLx-backed Postgres delegates for CrateStack models.

Overview

cratestack-sqlx provides async model delegates backed by PostgreSQL via SQLx. It's the server-side database layer that include_schema! generates delegate implementations for.

Installation

[dependencies]
cratestack-sqlx = "0.2"
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "chrono", "uuid"] }

Usage

Delegate Methods

Generated by include_schema!:

use cratestack::include_schema;

include_schema!("schema.cstack");

let pool = sqlx::PgPool::connect(&database_url).await?;
let cool = cratestack_schema::CrateStack::builder(pool).build();

// Find by ID
let user: Option<User> = cool.user().find_by_id(&ctx, user_id).await?;

// Find many with filters
let posts = cool.post()
    .find_many()
    .where_expr(
        post::published().is_true()
            .and(post::author().email().eq("owner@example.com"))
    )
    .order_by(post::createdAt().desc())
    .limit(20)
    .run(&ctx)
    .await?;

// Create
let user = cool.user().create(ctx, CreateUserInput {
    email: "user@example.com".to_owned(),
    name: "Alice".to_owned(),
}).await?;

// Update
let user = cool.user().update(ctx, user_id, UpdateUserInput {
    name: Some("Bob".to_owned()),
    ..Default::default()
}).await?;

// Delete
cool.user().delete(ctx, user_id).await?;

Projections

Select specific fields and relations:

let user = cool.user()
    .find_unique()
    .select(User::select()
        .id()
        .email()
        .include_posts(Post::include_selection()
            .id()
            .title()
        )
    )
    .run(&ctx)
    .await?;

Transactions

use sqlx::Acquire;

let mut tx = pool.begin().await?;

let account = Account::find_by_id(&mut *tx, from_id).await?;
Account::update_balance(&mut *tx, from_id, -amount).await?;
Account::update_balance(&mut *tx, to_id, amount).await?;

tx.commit().await?;

Transaction Isolation

Use explicit isolation for procedures:

mutation procedure transfer(amount: Decimal, to: String): Transfer {
  @isolation("serializable")
}
// Generated procedure wrapper handles isolation
let result = cool.transfer(ctx, TransferInput { amount, to }).await?;

Audit Integration

Enable audit on models:

model Transfer {
  id String @id
  amount Decimal
  
  @@audit
}

Audit events are recorded transactionally with the mutation via AuditSink.

Decimal Backend

Select backend at compile time:

[features]
default = ["decimal-rust-decimal"]  # 128-bit fixed
decimal-bigdecimal = ["cratestack-sqlx/decimal-bigdecimal"]

Related Crates

  • cratestack-core - Core types
  • cratestack-sql - Dialect-agnostic SQL primitives
  • cratestack-rusqlite - SQLite backend (on-device, sync)

License

MIT