Skip to content

nils-degroot/gremlin-orm

Repository files navigation

Gremlin ORM

A lightweight, type-safe ORM for PostgreSQL in Rust, built on top of SQLx with derive macro support for common CRUD operations.

Features

  • 🔒 Type-safe - Compile-time SQL query verification via SQLx
  • 🚀 Async/await - Full async support with streaming capabilities
  • 📝 Derive macros - Minimal boilerplate with #[derive(Entity)]
  • 🐘 PostgreSQL optimized - Leverages PostgreSQL-specific features
  • 🔄 CRUD operations - Insert, Update, Delete, and Stream entities
  • 🏗️ Generated fields - Support for auto-increment IDs and computed columns
  • 🗑️ Soft deletes - Mark records as deleted without removing them from the database

See the documentation on docs.rs

Quick Start

Add gremlin-orm and sqlx to your Cargo.toml:

[dependencies]
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio"] }
gremlin-orm = "0.1.0"

Usage

Define an Entity

use gremlin_orm::{Entity, InsertableEntity, UpdatableEntity, StreamableEntity, DeletableEntity};
use futures::StreamExt;

#[derive(Debug, Entity)]
#[orm(table = "public.users")]
struct User {
    #[orm(pk, generated)]
    id: i32,
    name: String,
    email: String,
    #[orm(generated)]
    created_at: chrono::DateTime<chrono::Utc>,
}

Field-level Annotations

  • #[orm(pk)]: Marks the field as a primary key. Multiple fields can be marked as primary keys for composite keys.

  • #[orm(generated)]: Indicates the field is auto-generated by the database (e.g., auto-increment or computed columns). Such fields are excluded from inserts and updates.

  • #[orm(deref)]: Used for optional/reference types (e.g., Option<T>, &str, etc.), allowing the macro to handle dereferencing when generating queries.

  • #[orm(default)]: Allows the field to use a default value when inserting, by wrapping it in Defaultable<T>.

  • #[orm(cast = "TYPE")]: Casts the field to the specified SQL type in generated queries. This is useful when you want to explicitly cast a column in SQL (e.g., for custom types or to resolve type mismatches).

    Example:

    #[derive(Entity)]
    #[orm(table = "public.example")]
    struct Example {
        #[orm(pk)]
        id: i32,
        #[orm(cast = "TEXT")]
        data: String,
    }

Basic Operations

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let pool = sqlx::PgPool::connect("postgresql://user:pass@localhost/db").await?;

    // Insert a new user
    let user = InsertableUser {
        name: "Alice".to_string(),
        email: "[email protected]".to_string(),
    }
    .insert(&pool)
    .await?;

    println!("Created user: {:?}", user);

    // Update the user
    let mut updatable = UpdatableUser::from(user);
    updatable.name = "Alice Smith".to_string();
    let updated_user = updatable.update(&pool).await?;

    // Stream all users
    let users: Vec<_> = User::stream(&pool)
        .map(|result| result.unwrap())
        .collect()
        .await;

    // Delete the user
    updated_user.delete(&pool).await?;

    Ok(())
}

License

This project is licensed under the GNU General Public License v3.0.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published