| Crates.io | torch-web |
| lib.rs | torch-web |
| version | 0.2.8 |
| created_at | 2025-07-10 15:43:33.179658+00 |
| updated_at | 2025-07-20 17:09:57.052738+00 |
| description | ๐ฅ Fast, secure web framework for Rust with Laravel-inspired features, built-in ORM, CLI tools, and production-ready security |
| homepage | https://github.com/Enigmatikk/torch |
| repository | https://github.com/Enigmatikk/torch |
| max_upload_size | |
| id | 1746663 |
| size | 869,317 |
The web framework that doesn't get in your way.
Torch is a fast, secure, and production-ready web framework for Rust. Built on Tokio and Hyper, it provides everything you need to build modern web applications with minimal configuration.
use torch_web::{App, Request, Response, main};
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let app = App::new()
.get("/", |_req: Request| async {
Response::ok().body("Hello, World! ๐ฅ")
})
.get("/users/:id", |req: Request| async move {
let id = req.param("id").unwrap_or("Anonymous");
Response::ok().body(format!("Hello, {}! ๐ฅ", id))
});
println!("๐ฅ Server running at http://localhost:3000");
app.listen("127.0.0.1:3000").await
}
Why developers choose Torch:
Path<T>, Query<T>, Json<T>use torch_web::{routes, get, Path, Query};
routes! {
#[get("/users/{id}")]
async fn get_user(Path(id): Path<u32>) -> Response {
Response::ok().json(format!("User {}", id))
}
#[get("/users")]
async fn list_users(Query(params): Query<UserQuery>) -> Response {
// Type-safe query parameter extraction
Response::ok().json(params)
}
}
@extends, @section, @foreach@include for reusable templatesuse torch_web::{ember::*, main};
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let app = App::new()
.get("/", |_req| async {
let data = EmberData::new()
.with("title", "Welcome to Torch")
.with("users", vec!["Alice", "Bob", "Charlie"]);
ember("home", data).await
});
app.listen("127.0.0.1:3000").await
}
Template file (templates/home.ember):
@extends('layout')
@section('content')
<h1>{{ $title }}</h1>
@if(count($users) > 0)
<ul>
@foreach($users as $user)
<li>๐ฅ {{ $user }}</li>
@endforeach
</ul>
@else
<p>No users found.</p>
@endif
@endsection
my-torch-app/
โโโ crates/
โ โโโ core/ # Business logic
โ โโโ web/ # Torch web application
โ โโโ auth/ # Authentication
โ โโโ database/ # Data layer
โ โโโ api/ # External integrations
:id, :name with automatic type conversion?key=value into structs or HashMaps@extends and @section.ember files# Install VS Code extension for .ember template support
code --install-extension enigmatikk.torch-ember
For full-featured applications with templates:
cargo add torch-web --features templates,json,database
For API-only applications:
cargo add torch-web --features json
For maximum features (production apps):
[dependencies]
torch-web = {
version = "0.2.8",
features = ["templates", "json", "database", "cache", "websocket"]
}
CLI Tool (Optional):
Install the Torch CLI for Laravel Artisan-like functionality:
cargo install torch-web --features cli
The CLI will automatically show PATH setup instructions for your operating system. If the torch command is not found after installation, follow the displayed instructions to add ~/.cargo/bin (or %USERPROFILE%\.cargo\bin on Windows) to your system PATH.
Quick CLI usage:
torch new my-app # Create new Torch application
cd my-app
torch serve --hot # Start development server with hot reload
torch make controller # Generate controllers, models, etc.
torch --help # Show all available commands
Available Features:
templates - Ember templating engine with Laravel Blade-like syntaxjson - JSON request/response handling with serdedatabase - PostgreSQL support with SQLxcache - Redis caching integrationwebsocket - WebSocket support for real-time appsapi - Enhanced API development toolscli - Command-line interface with Laravel Artisan-like functionalitycli - Laravel Artisan-inspired command-line toolsTorch includes a powerful CLI tool inspired by Laravel's Artisan, providing rapid scaffolding and development utilities:
# Install the CLI
cargo install torch-web --features cli
# Create a new Torch application
torch new my-app
# Generate controllers, models, and more
torch make controller UserController --resource
torch make model User --migration --factory --seeder
# Start development server with hot reload
torch serve --hot
# Run database migrations
torch migrate
# Interactive REPL for debugging
torch tinker
# Build for production
torch build --release
Project Management:
torch new <name> - Create new Torch applicationtorch serve --hot - Development server with hot reloadtorch build --release - Production build with optimizationsCode Generation:
torch make controller <name> - Generate controllers (with --resource, --api flags)torch make model <name> - Generate models (with --migration, --factory, --seeder flags)torch make middleware <name> - Generate middlewaretorch make template <name> - Generate Ember templatestorch make migration <name> - Generate database migrationstorch make test <name> - Generate testsDatabase Operations:
torch migrate - Run migrations (with rollback, fresh, status subcommands)torch db seed - Seed databasetorch db status - Show database statusDevelopment Tools:
torch tinker - Interactive REPL shelltorch route list - Show all routestorch cache clear - Clear application cachestorch optimize - Optimize for productionSee the CLI Documentation for complete command reference and CLI Tutorial for a step-by-step guide.
use torch_web::{App, Request, Response, main};
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let app = App::new()
.get("/", |_req: Request| async {
Response::ok().body("Hello, World! ๐ฅ")
})
.get("/hello/:name", |req: Request| async move {
let name = req.param("name").unwrap_or("Anonymous");
Response::ok().body(format!("Hello, {}! ๐ฅ", name))
})
.get("/json", |_req: Request| async {
#[cfg(feature = "json")]
{
use serde_json::json;
Response::ok()
.json(&json!({
"message": "Hello from Torch!",
"framework": "torch",
"version": "0.1.0"
}))
.unwrap()
}
#[cfg(not(feature = "json"))]
{
Response::ok()
.content_type("application/json")
.body(r#"{"message": "Hello from Torch!", "framework": "torch"}"#)
}
});
println!("๐ฅ Starting Torch Hello World example...");
app.listen("127.0.0.1:3000").await
}
# Clone the repository
git clone https://github.com/Enigmatikk/torch.git
cd torch
# Run the hello world example
cargo run --example hello_world
# Visit http://localhost:3000 to see it in action!
Torch includes Ember, a powerful templating engine inspired by Laravel's Blade but built for Rust performance:
use torch_web::{App, ember::*, main};
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let app = App::new()
.get("/", |_req| async {
let data = EmberData::new()
.with("title", "Welcome to Torch")
.with("users", vec!["Alice", "Bob", "Charlie"]);
ember("home", data).await
});
app.listen("127.0.0.1:3000").await
}
Create templates/home.ember:
@extends('layout')
@section('content')
<h1>{{ $title }}</h1>
@if(count($users) > 0)
<ul>
@foreach($users as $user)
<li>๐ฅ {{ $user }}</li>
@endforeach
</ul>
@else
<p>No users found.</p>
@endif
@endsection
Create templates/layout.ember:
<!DOCTYPE html>
<html>
<head>
<title>{{ $title }} - My App</title>
</head>
<body>
<div class="container">
@section('content')
<p>Default content</p>
@endsection
</div>
</body>
</html>
@extends, @section, @endsection@foreach, @if, @else, @endif@include('partial') for reusable templatesHere's a comprehensive example showing how Torch's features work together in a production-ready application:
use torch_web::{App, Request, Response, main, ember::*};
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
// Step 1: Basic Information
#[derive(Debug, Deserialize, Serialize, Clone)]
struct BasicInfo {
first_name: String,
last_name: String,
email: String,
phone: String,
}
// Registration wizard state
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
struct RegistrationData {
basic_info: Option<BasicInfo>,
current_step: u8,
}
// Session store (use Redis in production)
type SessionStore = std::sync::Arc<std::sync::Mutex<HashMap<String, RegistrationData>>>;
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let sessions: SessionStore = std::sync::Arc::new(std::sync::Mutex::new(HashMap::new()));
let sessions_clone = sessions.clone();
let app = App::new()
// Home page with beautiful template
.get::<_, ()>("/", |_req: Request| async {
let data = EmberData::new()
.with("title", "Welcome to Torch")
.with("page_title", "Multi-Step Registration Wizard")
.with("description", "Experience the power of Torch with Ember templating");
ember("home", data).await
})
// Registration wizard - Step 1
.get::<_, ()>("/register", move |req: Request| {
let sessions = sessions_clone.clone();
async move {
let session_id = get_or_create_session(&req);
let registration_data = get_session_data(&sessions, &session_id);
let data = EmberData::new()
.with("title", "Registration - Step 1")
.with("step", 1)
.with("step_title", "Basic Information")
.with("progress", 33)
.with("first_name", registration_data.basic_info
.as_ref().map(|b| b.first_name.clone()).unwrap_or_default());
ember("registration/step1", data).await
}
})
// Handle form submission with session state
.post::<_, ()>("/register/step1", move |req: Request| {
let sessions = sessions.clone();
async move {
let session_id = get_or_create_session(&req);
// Parse form data (simplified for example)
let basic_info = BasicInfo {
first_name: "John".to_string(),
last_name: "Doe".to_string(),
email: "john.doe@example.com".to_string(),
phone: "+1-555-0123".to_string(),
};
// Update session state
update_session_data(&sessions, &session_id, |data| {
data.basic_info = Some(basic_info);
data.current_step = 2;
});
// Redirect to next step
Response::redirect_found("/register/step2")
}
});
println!("๐ฅ Torch Registration Wizard starting...");
println!("๐ Visit http://localhost:3000 to see the demo");
app.listen("127.0.0.1:3000").await
}
// Helper functions for session management
fn get_or_create_session(req: &Request) -> String {
req.header("x-session-id").unwrap_or("demo-session").to_string()
}
fn get_session_data(sessions: &SessionStore, session_id: &str) -> RegistrationData {
let sessions = sessions.lock().unwrap();
sessions.get(session_id).cloned().unwrap_or_default()
}
fn update_session_data<F>(sessions: &SessionStore, session_id: &str, updater: F)
where F: FnOnce(&mut RegistrationData)
{
let mut sessions = sessions.lock().unwrap();
let mut data = sessions.get(session_id).cloned().unwrap_or_default();
updater(&mut data);
sessions.insert(session_id.to_string(), data);
}
Template Structure:
templates/
โโโ layouts/
โ โโโ main.ember # Base layout with CSS, header, footer
โโโ components/
โ โโโ header.ember # Navigation component
โ โโโ footer.ember # Footer component
โ โโโ progress_bar.ember # Reusable progress indicator
โโโ registration/
โโโ step1.ember # Extends main layout
โโโ step2.ember # Extends main layout
โโโ step3.ember # Extends main layout
Layout Template (templates/layouts/main.ember):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ $title }} - Torch Demo</title>
<style>
body { font-family: 'Segoe UI', sans-serif; margin: 0; }
.container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
/* Beautiful responsive CSS... */
</style>
</head>
<body>
@include('components/header')
<main class="container">
@section('content')
<p>Default content</p>
@endsection
</main>
@include('components/footer')
</body>
</html>
Step Template (templates/registration/step1.ember):
@extends('layouts/main')
@section('content')
@include('components/progress_bar')
<div class="form-container">
<h2>{{ $step_title }}</h2>
<form method="POST" action="/register/step1">
<div class="form-group">
<label>First Name</label>
<input type="text" name="first_name" value="{{ $first_name }}" required>
</div>
<div class="form-group">
<label>Last Name</label>
<input type="text" name="last_name" value="{{ $last_name }}" required>
</div>
<button type="submit">Continue to Step 2 โ</button>
</form>
</div>
@endsection
This example demonstrates:
@include directivesTorch features a powerful extractors system that makes handling requests type-safe and ergonomic:
use torch_web::{App, main, extractors::*};
use std::collections::HashMap;
#[derive(Clone)]
struct AppState {
counter: std::sync::Arc<tokio::sync::Mutex<u64>>,
}
#[main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let state = AppState {
counter: std::sync::Arc::new(tokio::sync::Mutex::new(0)),
};
let app = App::new()
.with_state(state)
// Path parameters
.get("/users/:id", |Path(user_id): Path<u32>| async move {
format!("User ID: {}", user_id)
})
// Query parameters
.get("/search", |Query(params): Query<HashMap<String, String>>| async move {
let query = params.get("q").unwrap_or(&"*".to_string());
format!("Searching for: {}", query)
})
// JSON body (with json feature)
.post("/users", |Json(user): Json<serde_json::Value>| async move {
format!("Creating user: {}", user)
})
// Headers
.get("/info", |Headers(headers): Headers| async move {
let user_agent = headers.get("user-agent")
.and_then(|v| v.to_str().ok())
.unwrap_or("Unknown");
format!("Your browser: {}", user_agent)
})
// Application state
.post("/increment", |State(state): State<AppState>| async move {
let mut counter = state.counter.lock().await;
*counter += 1;
format!("Counter: {}", *counter)
})
// Multiple extractors
.get("/api/:version/search", |
Path(version): Path<String>,
Query(params): Query<HashMap<String, String>>,
State(state): State<AppState>,
| async move {
let counter = state.counter.lock().await;
let query = params.get("q").unwrap_or(&"*".to_string());
format!("API v{}: Searching '{}' (requests: {})", version, query, *counter)
});
app.listen("127.0.0.1:3000").await
}
Path<T> - Extract path parameters (:id, :name, etc.)Query<T> - Extract query string parameters (?key=value)Json<T> - Extract and deserialize JSON request bodiesHeaders - Access request headersState<T> - Access shared application stateOne of Torch's standout features is its beautiful, Sinatra-inspired error pages:
Torch includes rotating 404 messages with flame themes:
use torch_web::ErrorPages;
let custom_pages = ErrorPages::new()
.custom_404("Your custom 404 HTML here")
.custom_500("Your custom 500 HTML here");
let app = App::new()
.error_pages(custom_pages);
Torch uses feature flags to keep your binary size small:
default - Includes JSON supportjson - JSON serialization with serdeproduction - All production features (monitoring, security, etc.)security - Security middleware and utilitieswebsocket - WebSocket and real-time featuresdatabase - PostgreSQL support with connection poolingcache - Redis caching integrationapi - API documentation generationconfig - TOML configuration supportmonitoring - Metrics and structured loggingTorch is built on proven Rust technologies:
Torch supports configuration through TOML files and environment variables.
Create a torch.toml file in your project root:
[server]
host = "0.0.0.0"
port = 8080
max_connections = 10000
request_timeout_secs = 30
[security]
enable_cors = true
enable_security_headers = true
enable_rate_limiting = true
per_ip_rps_limit = 100
[monitoring]
enable_metrics = true
enable_request_logging = true
log_level = "info"
[database]
url = "postgresql://user:pass@localhost/db"
max_connections = 10
[cache]
redis_url = "redis://localhost:6379"
default_ttl_secs = 3600
export TORCH_HOST=0.0.0.0
export TORCH_PORT=8080
export TORCH_DATABASE_URL=postgresql://user:pass@localhost/db
export TORCH_REDIS_URL=redis://localhost:6379
use torch_web::security::*;
// Input validation and sanitization
let app = App::new()
.middleware(InputValidator::new())
.middleware(SecurityHeaders::new())
.middleware(RateLimiter::new(100)); // 100 requests per second
// HMAC request signing
let signing = RequestSigning::new("your-secret-key");
let app = app.middleware(signing);
// IP whitelisting
let whitelist = IpWhitelist::new()
.allow_ip("192.168.1.1")
.allow_range("10.0.0.0/8");
let app = app.middleware(whitelist);
use torch_web::production::*;
// Metrics and monitoring
let app = App::new()
.middleware(MetricsCollector::new())
.middleware(PerformanceMonitor::new())
.middleware(RequestLogger::new());
// Health check endpoint
let app = app.get("/health", |_req| async {
Response::ok().json(&serde_json::json!({
"status": "healthy",
"uptime": "24h",
"version": "0.1.0"
})).unwrap()
});
Torch supports organizing large applications across multiple crates to prevent any single crate from becoming too large:
# Workspace Cargo.toml
[workspace]
members = [
"crates/core", # Business logic
"crates/web", # Torch web application
"crates/auth", # Authentication
"crates/database", # Data layer
"crates/api", # External integrations
]
[workspace.dependencies]
torch-web = { version = "0.2.8", features = ["templates", "json"] }
Benefits:
Leverage Rust's fantastic compiler for zero-cost route registration:
use torch_web::{routes, get, post, Path, Query, Json};
// Compile-time validated routes with type-safe extractors
routes! {
#[get("/users/{id}")]
async fn get_user(Path(id): Path<u32>) -> Response {
// id is guaranteed to be a valid u32 at compile time
Response::ok().json(format!("User {}", id))
}
#[get("/users")]
async fn list_users(Query(params): Query<UserQuery>) -> Response {
// params is type-checked at compile time
Response::ok().json(params)
}
#[post("/users")]
async fn create_user(Json(user): Json<CreateUserRequest>) -> Response {
// JSON deserialization is validated at compile time
Response::created().json("User created")
}
}
Compile-Time Benefits:
Build applications with consistent headers, footers, and menus across multiple pages:
// Shared layout with navigation
// templates/layouts/app.ember
```html
<!DOCTYPE html>
<html>
<head>
<title>{{ $title }} - My App</title>
<link rel="stylesheet" href="/css/app.css">
</head>
<body>
@include('components/header')
@include('components/navigation')
<main class="content">
@section('content')
<p>Default content</p>
@endsection
</main>
@include('components/footer')
</body>
</html>
// All pages inherit the same layout
// templates/users/index.ember
```html
@extends('layouts/app')
@section('content')
<h1>{{ $page_title }}</h1>
@foreach($users as $user)
<div class="user-card">{{ $user.name }}</div>
@endforeach
@endsection
Theming Benefits:
Torch provides a powerful and flexible middleware system:
use torch_web::middleware::*;
// Built-in middleware
let app = App::new()
.middleware(Logger::new())
.middleware(Cors::permissive())
.middleware(SecurityHeaders::new())
.middleware(Compression::new());
// Custom middleware
let app = app.middleware(|req: Request, next| {
Box::pin(async move {
let start = std::time::Instant::now();
let response = next(req).await;
let duration = start.elapsed();
println!("Request took: {:?}", duration);
response
})
});
Torch is built for speed and efficiency:
Why Torch is fast:
Benchmark it yourself:
# Clone the repository
git clone https://github.com/Enigmatikk/torch.git
cd torch
# Run the hello world example
cargo run --example hello_world --release
# Test with your favorite load testing tool
wrk -t12 -c400 -d30s http://localhost:3000/
# Clone and run in 30 seconds
git clone https://github.com/Enigmatikk/torch.git
cd torch
# Run the hello world example
cargo run --example hello_world
# Visit http://localhost:3000 to see:
# - Hello World endpoint
# - Path parameters (/hello/:name)
# - JSON responses (/json)
# - Beautiful 404 pages (try /nonexistent)
We welcome contributions! Please see our Contributing Guide for details.
# Clone the repository
git clone https://github.com/Enigmatikk/torch.git
cd torch
# Run tests
cargo test --all-features
# Run examples
cargo run --example hello_world # Basic routing
cargo run --example registration_wizard --features templates,json # Multi-step wizard
cargo run --example ember_demo --features templates # Template showcase
cargo run --example enhanced_extractors # Type-safe extractors
# Check formatting
cargo fmt --check
# Run clippy
cargo clippy --all-features
This project is licensed under the MIT OR Apache-2.0 license.
cargo run --example registration_wizard --features templates,jsonBuilt with โค๏ธ and ๐ฅ for developers who ship fast
Torch - The web framework that doesn't get in your way ๐ฅ