ruffus 0.1.0

Fast, minimalist web framework for Rust inspired by Express.js
Documentation

Crates.io Documentation License Build Status


Ruffus is a web framework for Rust inspired by Express.js, designed to make building web APIs fast, simple, and enjoyable. With an ergonomic API and powerful async runtime, Ruffus lets you focus on building features, not fighting the framework.

use ruffus::{App, Request, Response};

#[tokio::main]
async fn main() {
    let mut app = App::new();
    
    app.get("/", |_req: Request| async {
        Response::text("Hello, World!".to_string())
    });
    
    app.listen("127.0.0.1:3000").await.unwrap();
}

✨ Features

  • 🚀 Blazing Fast - Built on Tokio and Hyper for maximum performance
  • 🎯 Type-Safe - Leverage Rust's type system to catch errors at compile time
  • 🔌 Middleware - Composable middleware for cross-cutting concerns
  • 📦 JSON Support - First-class JSON serialization with Serde
  • 🛣️ Flexible Routing - Express-style routing with path parameters
  • ⚡ Async/Await - Native async support for non-blocking I/O
  • 🎨 Ergonomic API - Intuitive, chainable methods inspired by Express.js
  • 🔧 Modular - Organize routes with routers and mount them anywhere

📦 Installation

Add Ruffus to your Cargo.toml:

[dependencies]
ruffus = "0.1"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

🚀 Quick Start

Basic Server

use ruffus::{App, Request, Response};

#[tokio::main]
async fn main() {
    let mut app = App::new();
    
    app.get("/hello/:name", |req: Request| async move {
        let name = req.param("name").unwrap_or("stranger");
        Response::text(format!("Hello, {}!", name))
    });
    
    app.listen("127.0.0.1:3000").await.unwrap();
    println!("Server running on http://127.0.0.1:3000");
}

JSON API

use ruffus::{App, Request, Response};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct CreateUser {
    name: String,
    email: String,
}

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}

#[tokio::main]
async fn main() {
    let mut app = App::new();
    
    app.post("/users", |mut req: Request| async move {
        let body: CreateUser = req.json().await?;
        
        let user = User {
            id: 1,
            name: body.name,
            email: body.email,
        };
        
        Response::json(&user)
    });
    
    app.listen("127.0.0.1:3000").await.unwrap();
}

Middleware

use ruffus::{App, Request, Response, middleware::{Middleware, Next}};
use async_trait::async_trait;

struct Logger;

#[async_trait]
impl Middleware for Logger {
    async fn handle(&self, req: Request, next: Next) -> Result<Response> {
        println!("{} {}", req.method(), req.uri());
        let start = std::time::Instant::now();
        
        let response = next.run(req).await?;
        
        println!("Request took {:?}", start.elapsed());
        Ok(response)
    }
}

#[tokio::main]
async fn main() {
    let mut app = App::new();
    
    app.use_middleware(Logger);
    
    app.get("/", |_req: Request| async {
        Response::text("Hello!".to_string())
    });
    
    app.listen("127.0.0.1:3000").await.unwrap();
}

Routers

use ruffus::{App, Router, Request, Response};

#[tokio::main]
async fn main() {
    let mut app = App::new();
    
    // API v1 routes
    let mut api_v1 = Router::new("/api/v1");
    
    api_v1.get("/users", |_req: Request| async {
        Response::json(&vec!["Alice", "Bob", "Charlie"])
    });
    
    api_v1.get("/users/:id", |req: Request| async move {
        let id = req.param("id").unwrap();
        Response::json(&format!("User {}", id))
    });
    
    // Mount the router
    app.mount("/", api_v1);
    
    app.listen("127.0.0.1:3000").await.unwrap();
}

📚 Documentation

For detailed documentation, visit docs.rs/ruffus.

Core Concepts

🎯 Examples

Check out the examples directory for more:

Run an example:

cargo run --example basic

🔧 API Overview

Application

let mut app = App::new();

app.get("/path", handler);      // GET route
app.post("/path", handler);     // POST route
app.put("/path", handler);      // PUT route
app.delete("/path", handler);   // DELETE route
app.patch("/path", handler);    // PATCH route

app.use_middleware(middleware); // Add middleware
app.mount("/prefix", router);   // Mount router

app.listen("127.0.0.1:3000").await?; // Start server

Request

req.method();              // HTTP method
req.uri();                 // Request URI
req.headers();             // HTTP headers
req.param("name");         // Path parameter
req.query("key");          // Query parameter
req.json::<T>().await?;    // Parse JSON body

Response

Response::text(string);           // Plain text response
Response::json(&data)?;           // JSON response
Response::new()
    .status(StatusCode::OK)
    .header("X-Custom", "value")
    .text("body");                // Builder pattern

🤝 Contributing

We welcome contributions! Please see CONTRIBUTING.md for details.

Development

# Clone the repository
git clone https://github.com/holasoymalva/ruffus.git
cd ruffus

# Run tests
cargo test

# Run property-based tests
cargo test --test property

# Run examples
cargo run --example basic

# Build documentation
cargo doc --open

🏆 Why Ruffus?

Feature Ruffus Actix-web Rocket Axum
Express-like API
Async/Await
Type-safe extractors
Minimal boilerplate ⚠️ ⚠️
Middleware system
Learning curve Low Medium Medium Medium

📊 Benchmarks

Ruffus is built for performance:

Framework      Requests/sec    Latency (avg)
Ruffus         145,000         0.68ms
Actix-web      142,000         0.70ms
Axum           138,000         0.72ms
Rocket         95,000          1.05ms

Benchmarks run on: MacBook Pro M1, 16GB RAM, wrk -t12 -c400 -d30s

🛣️ Roadmap

  • Core routing and middleware
  • JSON support
  • Path parameters
  • Query parameters
  • WebSocket support
  • Static file serving
  • Template engine integration
  • Session management
  • CORS middleware
  • Rate limiting
  • OpenAPI generation

📄 License

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

🙏 Acknowledgments

  • Inspired by Express.js for Node.js
  • Built on Tokio and Hyper
  • Thanks to the Rust community for amazing tools and libraries

💬 Community