An enhanced web framework built on top of Axum, providing additional features and conveniences for building robust web applications in Rust.
- π Built on Axum - High performance and reliability
- π Automatic OpenAPI - Generate documentation from your code
- π Prometheus Metrics - Built-in metrics collection
- π CORS Support - Cross-origin resource sharing
- π WebSocket & SSE - Real-time endpoints, re-exported and ready
- π Static Files - Serve static content effortlessly
- β° Task Scheduling - Cron and interval-based background tasks
- π Message System - Built-in inter-service communication
- βοΈ Smart Configuration - Environment-based config with variable resolution
- ποΈ Two APIs - Choose between simple builder API or advanced trait-based API
use gotcha::prelude::*;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Gotcha::new()
.get("/", || async { "Hello World" })
.get("/hello/{name}", |Path(name): Path<String>| async move {
format!("Hello, {}!", name)
})
.post("/users", |Json(user): Json<User>| async move {
Json(user) // Echo the user back
})
.listen("127.0.0.1:3000")
.await?;
Ok(())
}
#[derive(Serialize, Deserialize)]
struct User {
name: String,
email: String,
}use gotcha::prelude::*;
#[config]
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Config {
pub database_url: String,
pub redis_url: String,
}
#[state]
#[derive(Clone, Default)]
pub struct AppState {
pub started_at: u64,
}
pub struct App {}
impl GotchaApp for App {
type State = AppState;
type Config = Config;
fn routes(&self, router: GotchaRouter<GotchaContext<Self::State, Self::Config>>)
-> GotchaRouter<GotchaContext<Self::State, Self::Config>> {
router
.get("/", hello_world)
.get("/users/{id}", get_user)
}
async fn state(&self, config: &ConfigWrapper<Self::Config>) -> GotchaResult<Self::State> {
// Open database connections here; `config` is already loaded.
let _ = &config.database_url;
Ok(AppState::default())
}
}
// The application's own config and state extract directly, thanks to `#[config]` / `#[state]`.
async fn hello_world(State(config): State<Config>) -> impl Responder {
config.redis_url.clone()
}
async fn get_user(Path(id): Path<u32>, State(_state): State<AppState>) -> impl Responder {
format!("user {id}")
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
App {}.run().await?;
Ok(())
}Add Gotcha to your Cargo.toml:
[dependencies]
gotcha = "0.4"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }Enable additional features as needed:
[dependencies]
gotcha = { version = "0.4", features = ["openapi", "prometheus", "cors", "static_files", "task"] }Available features:
openapi- Automatic OpenAPI/Swagger documentationprometheus- Metrics collection and expositioncors- Cross-Origin Resource Sharing supportstatic_files- Static file serving capabilitiestask- Background task scheduling with cron support
With the openapi feature enabled, use the #[api] macro for automatic documentation:
use gotcha::prelude::*;
#[derive(Schematic, Serialize, Deserialize)]
struct User {
id: u32,
name: String,
email: String,
}
/// Get user by ID
#[api(id = "get_user", group = "users")]
async fn get_user(Path(id): Path<u32>) -> Json<User> {
Json(User { id, name: "Ada".into(), email: "ada@example.com".into() })
}Visit these endpoints when running:
/redoc- ReDoc documentation interface/scalar- Scalar documentation interface/openapi.json- Raw OpenAPI specification
Create a configurations/application.toml file. Your application's own settings live at the top
level; the framework's are in the reserved [server] section:
database_url = "${DATABASE_URL}"
api_key = "${API_KEY}"
app_name = "My Gotcha App"
[server]
host = "127.0.0.1"
port = 3000Mark your config type with #[config] to extract it directly in handlers:
use gotcha::prelude::*;
#[config]
#[derive(Clone, Default, Serialize, Deserialize)]
struct Config {
app_name: String,
}
async fn handler(State(config): State<Config>) -> impl Responder {
config.app_name.clone()
}The server settings are their own extractor, State<ServerConfig>; State<ConfigWrapper<Config>>
still gives you both at once and derefs to your config.
Configuration supports:
-
Environment variable resolution inside values:
${ENV_VAR} -
Path variable resolution:
${app.database.name} -
Profile-based overrides via
GOTCHA_ACTIVE_PROFILEenvironment variable -
Environment overrides with the
APP_prefix, where__separates nested sections:variable overrides APP_APP_NAME=xthe top-level app_namefieldAPP_SERVER__PORT=8080portinside[server]A single underscore stays part of the field name, so snake_case fields are addressable, and typed fields (numbers, booleans) parse the value rather than rejecting it.
Requires the task feature.
use gotcha::prelude::*;
use std::time::Duration;
# pub struct App {}
impl GotchaApp for App {
type State = ();
type Config = EmptyConfig;
fn routes(&self, router: GotchaRouter<GotchaContext<Self::State, Self::Config>>)
-> GotchaRouter<GotchaContext<Self::State, Self::Config>> {
router
}
async fn state(&self, _config: &ConfigWrapper<Self::Config>) -> GotchaResult<Self::State> {
Ok(())
}
async fn tasks(&self, scheduler: &mut TaskScheduler<Self::State, Self::Config>) -> GotchaResult<()> {
// Daily cleanup at 2 AM (cron fields: sec min hour day month weekday)
scheduler.cron("cleanup", "0 0 2 * * *".to_string(), |_ctx| async {
println!("Running cleanup task");
});
// Every 30 seconds
scheduler.interval("heartbeat", Duration::from_secs(30), |_ctx| async {
println!("Heartbeat");
});
Ok(())
}
}Gotcha is organized as a Rust workspace with the following structure:
gotcha/
βββ gotcha/ # Main framework crate
βββ gotcha_macro/ # Procedural macros
βββ examples/ # Example applications
βββ basic/ # Basic usage example
βββ openapi/ # OpenAPI documentation example
βββ configuration/# Configuration management example
βββ task/ # Background tasks example
βββ message/ # Message system example
βββ simple/ # Builder API example
- GotchaApp trait - Main application interface for complex apps
- Gotcha builder - Simple API for straightforward applications
- GotchaRouter - Enhanced Axum router with OpenAPI integration
- GotchaContext - Application context combining state and configuration
- ConfigWrapper - Configuration management with environment resolution
# Build main crate
cargo build --package gotcha
# Build with all features
cargo build --all-features
# Test all feature combinations
python3 test-feature-matrix.py# Run tests
cargo test --package gotcha
# Test with specific features
cargo test --package gotcha --features "openapi prometheus"# Format code
cargo fmt
# Run linter
cargo clippy --all-targets
# Generate documentation
cargo doc --openRun any example to see Gotcha in action:
cd examples/simple && cargo run # Builder API showcase
cd examples/openapi && cargo run # OpenAPI documentation
cd examples/task && cargo run # Background tasks
cd examples/message && cargo run # Message system- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
python3 test-feature-matrix.py - Submit a pull request
This project is licensed under the MIT License - see the LICENSE file for details.