Skip to content

Repository files navigation

Gotcha

An enhanced web framework built on top of Axum, providing additional features and conveniences for building robust web applications in Rust.

Crates.io Documentation License: MIT

✨ Features

  • πŸš€ 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

πŸš€ Quick Start

Simple Builder API (Recommended for new projects)

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,
}

Advanced Trait API (For complex applications)

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(())
}

πŸ“¦ Installation

Add Gotcha to your Cargo.toml:

[dependencies]
gotcha = "0.4"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }

Optional Features

Enable additional features as needed:

[dependencies]
gotcha = { version = "0.4", features = ["openapi", "prometheus", "cors", "static_files", "task"] }

Available features:

  • openapi - Automatic OpenAPI/Swagger documentation
  • prometheus - Metrics collection and exposition
  • cors - Cross-Origin Resource Sharing support
  • static_files - Static file serving capabilities
  • task - Background task scheduling with cron support

πŸ“– Documentation & Examples

OpenAPI Documentation

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

Configuration System

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 = 3000

Mark 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_PROFILE environment variable

  • Environment overrides with the APP_ prefix, where __ separates nested sections:

    variable overrides
    APP_APP_NAME=x the top-level app_name field
    APP_SERVER__PORT=8080 port inside [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.

Task Scheduling

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(())
    }
}

πŸ—οΈ Architecture

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

Core Concepts

  • 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

πŸ”§ Development

Building

# Build main crate
cargo build --package gotcha

# Build with all features
cargo build --all-features

# Test all feature combinations
python3 test-feature-matrix.py

Testing

# Run tests
cargo test --package gotcha

# Test with specific features
cargo test --package gotcha --features "openapi prometheus"

Code Quality

# Format code
cargo fmt

# Run linter
cargo clippy --all-targets

# Generate documentation
cargo doc --open

πŸ“š Examples

Run 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

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Run tests: python3 test-feature-matrix.py
  5. Submit a pull request

πŸ“„ License

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

πŸ”— Related Projects

  • Axum - The underlying web framework
  • mofa - Configuration management
  • oas - OpenAPI schema generation

About

full featured web framework written by rust

Topics

Resources

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages