bloom-web 0.1.2

A lightweight backend framework combining Actix-Web, SQLx, and declarative macros for rapid API development
docs.rs failed to build bloom-web-0.1.2
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: bloom-web-0.1.0

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

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:

    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

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

#[repository(User)]
pub struct UserRepository;

4. Add REST endpoints

#[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

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

#[derive(Entity)]
#[table("my_table")]
struct MyEntity {
    // ...
}

Field Configuration

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

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

#[get_mapping("/users/{id}")]
pub async fn get_user(path: web::Path<i64>) -> impl Responder {
    // id is automatically extracted from URL
}

JSON Payloads

#[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:

#[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

#[scheduled(60000)] // Run every 60 seconds
pub async fn cleanup_job(pool: &MySqlPool) {
    // Background job logic
}

OpenAPI/Swagger Integration

// 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

cargo build

Run tests

cargo test

Build documentation

cargo doc --open

Check the project

cargo check

Advanced Features

Complex Entity Relationships

#[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

# 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.