bloom-web 0.1.6

A lightweight backend framework combining Actix-Web, SQLx, and declarative macros for rapid API development
# Bloom (Rust, Actix-Web, SQLx)


A lightweight backend framework that focuses on developer ergonomics by combining:
- Actix-Web for high‑performance HTTP
- SQLx (MySQL) for async, compile‑time checked database access
- Declarative macros to auto‑register controllers/routes, entities/migrations, and scheduled jobs
- Inventory-based discovery: drop in code, it self-registers
- Optional OpenAPI generation and Swagger UI via utoipa
- Simple, timestamped logging macro
- Config-first setup using a TOML file (config.toml)

## Features

- Convention-over-configuration controller macro: implement methods and have routes auto-wired
- Route mapping macros (get_mapping, post_mapping, put_mapping, delete_mapping, patch_mapping)
- Entity derive macro that can generate basic migration SQL and CRUD helper bindings
- Repository attribute macro to generate common CRUD helpers for an entity
- Inventory registries for:
    - Controllers (auto-configured onto Actix service)
    - Entities/migrations (run at boot)
    - Scheduled jobs (spawned on boot)
- Built-in optional Swagger UI and OpenAPI JSON
- Flexible CORS controlled via config.toml

## 🚀 Quick Start with Bloom Initializr


To quickly start a new Bloom project, clone the [Bloom Initializr repository](https://github.com/matusmesko/Bloom-initializr).  
It provides all necessary Cargo packages, a ready-to-use `main` function, and example projects to help you get started immediately.

## Architecture


The Bloom framework consists of three main components:

### 1. `bloom-core`

Contains the core framework functionality:
- Application builder and configuration
- Entity registry for database models
- Controller registry for HTTP handlers  
- Swagger documentation generation
- Scheduler for background tasks
- Logging utilities

### 2. `bloom-macros`

Procedural macros for code generation:
- `#[derive(Entity)]` - Generates database entity code
- `#[get_mapping]`, `#[put_mapping]`, etc. - HTTP route mapping
- `#[repository(Entity)]` - Repository pattern implementation
- `#[scheduled(interval)]` - Background job scheduling

### 3. `bloom` (main crate)

The unified API that ties everything together:
- Re-exports all functionality from sub-crates
- Provides convenient prelude module
- Main entry point for users

## Quick Start


1. Configure your database and server port in config.toml at the repository root:

   ```toml
   port = 8080
   database_url = "mysql://user:pass@host:3306/dbname"

   [cors]
   enabled = true
   allowed_origins = ["http://localhost:3000"]
   allowed_methods = ["GET", "POST", "PUT", "DELETE", "PATCH"]
   allowed_headers = ["Content-Type", "Authorization"]
   allow_credentials = true
   max_age = 3600
   ```

### 2. Create your first entity


```rust
use bloom::prelude::*;

#[derive(Entity, Serialize, Deserialize, Debug)]

#[table("users")]

struct User {
    #[id]
    id: i32,
    
    #[column("username")]
    username: String,
    
    #[column("email")]
    email: String,
}
```

### 3. Create a repository


```rust
#[repository(User)]

pub struct UserRepository;
```

### 4. Add REST endpoints


```rust
#[get_mapping("/users")]

pub async fn get_all_users(pool: web::Data<MySqlPool>) -> impl Responder {
    match UserRepository::find_all_raw(pool.get_ref()).await {
        Ok(rows) => {
            let json = serde_json::Value::Array(rows.into_iter().map(|row| {
                serde_json::json!({
                    "id": row.try_get::<i32,_>("id").unwrap_or_default(),
                    "username": row.try_get::<String,_>("username").unwrap_or_default(),
                    "email": row.try_get::<String,_>("email").unwrap_or_default(),
                })
            }).collect());
            HttpResponse::Ok().json(json)
        }
        Err(_) => HttpResponse::InternalServerError().finish()
    }
}

#[put_mapping("/users/{id}")]

pub async fn update_user(
    path: web::Path<i64>, 
    payload: web::Json<UpdateUser>, 
    pool: web::Data<MySqlPool>
) -> impl Responder {
    let id = path.into_inner();
    let data = payload.into_inner();
    let user = User { 
        id: id as i32, 
        username: data.username, 
        email: data.email,
    };
    match UserRepository::update(pool.get_ref(), &user).await {
        Ok(affected) if affected > 0 => HttpResponse::Ok().finish(),
        Ok(_) => HttpResponse::NotFound().finish(),
        Err(_) => HttpResponse::InternalServerError().finish(),
    }
}
```

### 5. Set up your application


```rust
use bloom::application;

#[tokio::main]

async fn main() -> anyhow::Result<()> {
    application::run().enable_swagger().await
}
```

## Requirements

- Rust toolchain (stable)
- MySQL compatible database

## Entity Attributes


The `#[derive(Entity)]` macro supports various attributes:

### Table Configuration

```rust
#[derive(Entity)]

#[table("my_table")]

struct MyEntity {
    // ...
}
```

### Field Configuration

```rust
struct User {
    #[id]                    // Primary key
    id: i32,
    
    #[column("user_name")]   // Custom column name
    name: String,
    
    // Regular field (uses field name as column name)
    email: String,
}
```

### Relationships

```rust
struct User {
    #[id]
    id: i32,
    
    #[one_to_many]
    posts: Vec<Post>,
    
    #[many_to_one]
    #[join_column("role_id")]
    role: Role,
}
```

## HTTP Route Mapping


Available route mapping macros:
- `#[get_mapping("/path")]`
- `#[post_mapping("/path")]`
- `#[put_mapping("/path")]`
- `#[delete_mapping("/path")]`
- `#[patch_mapping("/path")]`

### Path Parameters

```rust
#[get_mapping("/users/{id}")]

pub async fn get_user(path: web::Path<i64>) -> impl Responder {
    // id is automatically extracted from URL
}
```

### JSON Payloads

```rust
#[derive(Deserialize, utoipa::ToSchema, ApiSchema)]

pub struct CreateUser {
    pub username: String,
    pub email: String,
}

#[post_mapping("/users")]

pub async fn create_user(payload: web::Json<CreateUser>) -> impl Responder {
    // Automatic JSON deserialization
}
```

## Repository Pattern


The `#[repository(Entity)]` macro generates common CRUD operations:

```rust
#[repository(User)]

pub struct UserRepository;

// Generated methods:
// - find_all_raw(pool) -> Result<Vec<MySqlRow>>
// - find_by_id_raw(pool, id) -> Result<Option<MySqlRow>>
// - exists_by_id(pool, id) -> Result<bool>
// - count(pool) -> Result<i64>
// - delete_by_id(pool, id) -> Result<u64>
// - create(pool, entity) -> Result<u64>
// - update(pool, entity) -> Result<u64>
// - insert_or_update(pool, entity) -> Result<u64>
```

## Scheduled Jobs


```rust
#[scheduled(60000)] // Run every 60 seconds

pub async fn cleanup_job(pool: &MySqlPool) {
    // Background job logic
}
```

## OpenAPI/Swagger Integration


```rust
// Add to your struct for automatic API documentation
#[derive(Deserialize, utoipa::ToSchema, ApiSchema)]

pub struct UpdateUser {
    pub username: String,
    pub email: String,
}
```

Access Swagger UI at: `http://localhost:8080/swagger-ui/`

## Configuration



## Building and Testing


### Build the project

```bash
cargo build
```

### Run tests

```bash
cargo test
```

### Build documentation

```bash
cargo doc --open
```

### Check the project

```bash
cargo check
```


## Advanced Features


### Complex Entity Relationships


```rust
#[derive(Entity)]

#[table("users")]

pub struct User {
    #[id]
    id: i32,
    
    // One-to-many relationship (virtual)
    #[one_to_many]
    posts: Vec<Post>,
    
    // One-to-one relationship (virtual)
    #[one_to_one]
    profile: Option<Box<Profile>>,
}

#[derive(Entity)]

#[table("posts")]  

pub struct Post {
    #[id]
    id: i32,
    
    // Many-to-one with foreign key
    #[many_to_one]
    #[join_column("user_id")]
    user: User,
    
    title: String,
    content: String,
}
```

### Auto-Registration System


All components use Rust's `inventory` crate for automatic registration:
- Entities automatically register their table creation functions
- Routes automatically register with the HTTP server
- Repositories are automatically available
- Scheduled jobs automatically start on server boot

No manual configuration required - just add the macros and everything works!

## Production Notes


### Performance

- Built on Actix-Web, one of the fastest Rust web frameworks
- Compile-time SQL validation with SQLx
- Zero-cost abstractions through procedural macros

### Security

- Type-safe SQL queries prevent injection attacks
- Compile-time verification of database schemas
- Proper error handling with Result types

### Deployment

```bash
# Build optimized release version

cargo build --release

# The binary will be in target/release/

```


This project is licensed under the MIT License - see the LICENSE file for details.