Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

🚀 Revolutionary Relations System

d1-rs provides the world's most advanced, completely type-safe relations system that EXCEEDS every ORM in existence. We are the first ORM in any language to provide compile-time safe nested eager loading, recursive relationships, and rich M2M junction entities.

🏆 Revolutionary Relation Types - World Firsts

One-to-One Relations with Compile-Time Safety

User 1 ←→ 1 Profile  // ✅ All access compile-time validated

One-to-Many Relations with Advanced Eager Loading

User 1 ←→ ∞ Post     // ✅ Nested eager loading: .with_posts(|p| p.with_categories())

Many-to-Many Relations with Rich Junction Entities

Post ∞ ←→ ∞ Category  // ✅ Junction tables as first-class entities with full CRUD

🔥 World's First: Recursive Self-Referential Relations

Category ∞ ←→ ∞ Category  // ✅ parent/children with compile-time safety
User 1 ←→ ∞ User          // ✅ manager/employees with automatic null handling
Comment 1 ←→ ∞ Comment    // ✅ parent_comment/replies with unlimited depth

🚀 Revolutionary Key Features - Unmatched by Any ORM

🏆 World's First: Compile-Time Safe Nested Eager Loading

// ✅ IMPOSSIBLE in any other ORM - Compile-time validated nested relations!
let users_with_data = User::query()
    .with_posts(|posts| posts.with_categories())  // ✅ Nested with validation!
    .with_profile()
    .all(&db).await?;

// ✅ REVOLUTIONARY: Unlimited nesting depth
let complex_data = User::query()
    .with_posts(|posts| posts
        .with_categories(|categories| categories
            .with_parent()  // ✅ Recursive relations in nested loading!
        )
        .with_comments(|comments| comments
            .with_replies()  // ✅ Recursive comments in nested loading!
        )
    )
    .all(&db).await?;

🔥 World's First: Recursive Relationships with Type Safety

// ✅ Category hierarchy with compile-time safety
let root_children = root_category.children().all(&db).await?;
let parent_category = child.parent().first(&db).await?;

// ✅ User management hierarchy  
let manager = employee.manager().first(&db).await?;
let team_members = manager.employees().all(&db).await?;

// ✅ Comment reply threads
let comment_replies = comment.replies().all(&db).await?;
let parent_comment = reply.parent_comment().first(&db).await?;

// This would cause a compile error - method doesn't exist
let invalid = user.invalid_relation(); // ❌ Compile-time error

🚀 World's First: Rich M2M Junction Entities

// ✅ Junction entities as first-class citizens with full Entity powers!
#[derive(Entity)]
struct UserRole {
    id: i64,
    user_id: i64, 
    role_id: i64,
    granted_at: DateTime<Utc>,     // ✅ Rich additional data!
    granted_by: String,            // ✅ Who granted the role?
    expires_at: Option<DateTime<Utc>>, // ✅ Role expiration?
    is_active: bool,              // ✅ Role status?
}

// ✅ Query junction entities directly - IMPOSSIBLE in other ORMs!
let active_roles = UserRole::query()
    .where_is_active_eq(true)                    // ✅ Type-safe boolean!
    .where_expires_at_gt(current_timestamp)      // ✅ Type-safe timestamp!
    .all(&db).await?;

// ✅ Navigate through junction entities
let user_from_junction = user_role.user().first(&db).await?;
let role_from_junction = user_role.role().first(&db).await?;

🎯 Zero String Literals with Perfect Type Safety

// Type-safe association methods - NO STRING LITERALS ANYWHERE!
let user_posts = user.posts().all(&db).await?;
let post_count = user.posts().count(&db).await?;
let first_post = user.posts().first(&db).await?;

// Many-to-one: Post belongs to User  
let post_author = post.user().first(&db).await?;

// Many-to-many through junction entities
let post_categories = post.post_categories().all(&db).await?;

Migration Auto-Generation

Junction tables and relations are created automatically from your entity definitions:

let migration = SchemaMigration::new("blog".to_string())
    // Create your entity tables normally...
    .auto_generate_for::<User>()     // Generates all User relations
    .auto_generate_for::<Post>()     // Generates all Post relations  
    .auto_generate_for::<Category>(); // Generates all Category relations

Setting Up Relations

Relations are defined using the simple relations! macro - no complex migration code needed:

use d1_rs::*;

#[derive(Debug, Serialize, Deserialize, Clone, Entity, PartialEq)]
pub struct User {
    #[primary_key] pub id: i64,
    pub name: String,
    pub email: String,
}

#[derive(Debug, Serialize, Deserialize, Clone, Entity, PartialEq)]
pub struct Post {
    #[primary_key] pub id: i64,
    pub user_id: i64,
    pub title: String,
    pub content: String,
}

// Define relations with one simple macro - NO STRING LITERALS!
relations! {
    User {
        has_many posts: Post via user_id,
    }
    
    Post {
        belongs_to user: User via user_id,
    }
}

// That's it! Now you have type-safe association methods
let user_posts = user.posts().all(&db).await?;
let post_author = post.user().first(&db).await?;

Working with Relations

Association Methods

The relations! macro generates type-safe methods for each relation:

// Get all related records
let all_posts = user.posts().all(&db).await?;

// Count related records
let post_count = user.posts().count(&db).await?;

// Get first related record
let latest_post = user.posts().first(&db).await?;

// Many-to-one relations
let post_author = post.user().first(&db).await?;

// Many-to-many relations  
let post_categories = post.categories().all(&db).await?;

Loaded Data Access

If relations are pre-loaded, access them without database queries:

// Check if data is already loaded
if let Some(posts) = user.posts().loaded() {
    println!("Already have {} posts loaded", posts.len());
} else {
    // This will query the database
    let posts = user.posts().all(&db).await?;
}

Many-to-Many Relations

For many-to-many relations, simply define them in the macro and let d1-rs handle everything:

#[derive(Debug, Serialize, Deserialize, Clone, Entity, PartialEq)]
#[table(name = "post_categories")]  // Specify junction table name
pub struct PostCategory {
    #[primary_key] pub id: i64,
    pub post_id: i64,
    pub category_id: i64,
}

relations! {
    Post {
        has_many_through categories: Category via post_id,
    }
    
    Category {
        has_many_through posts: Post via category_id,
    }
}

// Junction table is created automatically with migration auto-generation
let migration = SchemaMigration::new("blog".to_string())
    .auto_generate_for::<Post>()
    .auto_generate_for::<Category>();

Managing Relations

Attach/Detach (Many-to-Many)

// Attach a category to a post
post.categories().attach(&db, category.id).await?;

// Detach a category from a post
post.categories().detach(&db, category.id).await?;

Performance Considerations

Lazy Loading

Each association method call executes a separate query:

// This executes one query per user - N+1 problem
for user in users {
    let posts = user.posts().all(&db).await?; 
}

Future versions will include eager loading capabilities to solve N+1 queries efficiently.

Next Steps

Ready to implement relations in your application? Check out specific guides:

The new type-safe API eliminates the need for string literals and provides compile-time safety that wasn't possible with the old traversal system.