ormkit 0.3.0

A compile-time safe async ORM for PostgreSQL powered by SQLx
Documentation

ormkit

A compile-time safe async ORM for PostgreSQL powered by SQLx.

Core ORM Features

  • Type-Safe Query DSL: Compile-time checked queries with fluent API
  • Entity Derive: #[derive(ormkit::Entity)] for automatic trait implementation
  • Repository Pattern: Generic CRUD operations with type-safe IDs
  • Query Builder: Dynamic filters and ordering with type safety
  • Pagination: Offset and cursor-based pagination
  • Transactions: Automatic commit/rollback with nested transaction support

Optional Production Features

Enable with feature flags (see Installation):

  • Migrations (migrations): Version-controlled schema migrations with CLI
  • Schema Validation (validate): Runtime entity-to-database validation
  • Performance (performance): Query/entity caching, parallel execution, smart pooling
  • Relationships: BelongsTo and HasMany with N+1 prevention (basic implementation)

Installation

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

Feature Flags

# Core ORM only (minimal dependencies)
ormkit = { version = "0.2", features = ["orm"] }

# Core + migrations (recommended for most projects)
ormkit = { version = "0.2", features = ["orm", "migrations"] }

# Core + production features
ormkit = { version = "0.2", features = ["full"] }

# All features (default)
ormkit = { version = "0.2" }

Quick Start

use ormkit::{Entity, Repository};
use ormkit::active_value::ActiveValue;
use sqlx::PgPool;
use uuid::Uuid;

// Define an Entity using the derive macro
#[derive(ormkit::Entity, Debug, Clone)]
#[ormkit(table = "users")]
struct User {
    #[ormkit(id)]
    id: Uuid,
    email: String,
    name: String,
    age: i32,
    created_at: chrono::DateTime<chrono::Utc>,
}

// Repository operations (CRUD)
async fn repository_example(pool: &PgPool) -> Result<(), Box<dyn std::error::Error>> {
    let repo = Repository::<User>::new(pool);

    // Find by ID
    let user = repo.find_by_id(user_id).await?;

    // Insert with ActiveModel
    let mut user_model = UserActiveModel::default();
    user_model.email = ActiveValue::Set("user@example.com".to_string());
    user_model.name = ActiveValue::Set("John Doe".to_string());
    let user = repo.insert(user_model).await?;

    // Update (only changed fields are updated)
    let mut user_model = user.into_active_model();
    user_model.name = ActiveValue::Set("Jane Doe".to_string());
    repo.update(user_model).await?;

    Ok(())
}

Core Features

Type-Safe Query Builder (Recommended)

use ormkit::Entity;

// Compile-time type-safe queries
let users: Vec<User> = User::query()
    .filter(User::email().eq("user@example.com"))
    .filter(User::age().gte(18))
    .order_by(User::created_at(), ormkit::query::Order::Desc)
    .limit(10)
    .fetch_all(&pool)
    .await?;

Dynamic Query Builder (for admin/filters)

use ormkit::{Filter, FilterOp, Order};

// Runtime query construction (for admin panels, dynamic filters)
let mut query = User::query();
if let Some(email) = email_filter {
    query = query.filter("email", FilterOp::Eq, email);
}
if let Some(min_age) = min_age {
    query = query.filter("age", FilterOp::Gte, min_age);
}
let users = query.fetch_all(&pool).await?;

Pagination

use ormkit::pagination::PaginationRequest;

let repo = Repository::<User>::new(&pool);
let request = PaginationRequest::new(1, 25); // page 1, 25 per page
let page = repo.paginate(&request).await?;

println!("Page {} of {}", page.meta.page, page.meta.total_pages);
for user in page.items {
    println!("User: {}", user.email);
}

Batch Operations

use ormkit::batch::{insert_many, BatchOptions};

let new_users = vec![/* ... */];
let options = BatchOptions::new().batch_size(50);
let result = insert_many(&pool, &new_users, Some(options)).await?;

println!("Inserted {} users", result.successful);

Transactions

use ormkit::transaction::transaction;

let result = transaction(&pool, |tx| async move {
    let repo = Repository::<User>::new(&tx);

    let user = repo.find_by_id(user_id).await?;
    let mut user_model = user.into_active_model();
    user_model.name = ActiveValue::Set("Updated Name".to_string());

    repo.update(user_model).await?;

    Ok::<(), Box<dyn std::error::Error>>(())
}).await?;

Advanced Features

Relationships (Prevents N+1 Queries)

use ormkit::relations::RelationBuilder;

// Load related entities in a single query
let posts_with_users = RelationBuilder::for_entities(posts)
    .load::<User>(&pool)
    .await?;

for loaded in posts_with_users {
    println!("Post: {} by {}", loaded.entity.title, loaded.related.name);
}

Project Status

v0.2.1 - Stable core ORM with production features

Production Ready

  • Entity derive macros with compile-time type safety
  • Type-safe query DSL with fluent API
  • Repository CRUD operations
  • Query builder with dynamic filters
  • Pagination (offset and cursor-based)
  • Transaction management with nested transactions
  • Migration engine with CLI
  • Schema validation

Performance Characteristics

  • Matches SQLx for single queries
  • Can outperform naive SQLx usage in specific scenarios:
    • Cached lookups (up to 2000x speedup on warm cache)
    • Bulk operations with parallel execution
    • N+1 query prevention
  • Raw SQLx remains fastest for single, one-off queries

In Development

  • Relationship ergonomics (BelongsTo/HasMany work, API needs polish)
  • One-to-one and many-to-many relationships
  • Typed ActiveModel derive macro integration

Platform Support

  • PostgreSQL: 12, 13, 14, 15, 16
  • Rust: 1.70+ (MSRV)
  • Tokio: 1.x

Documentation

Getting Started (5-10 min read)

In-Depth Guides

Production Topics

Reference

License

MIT OR Apache-2.0


Ormkit — A compile-time safe async ORM for PostgreSQL powered by SQLx.