Skip to main content

Crate gotcha

Crate gotcha 

Source
Expand description

Β§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

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:

    variableoverrides
    APP_APP_NAME=xthe top-level app_name field
    APP_SERVER__PORT=8080port 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;

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.

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

Re-exportsΒ§

pub use config::ConfigWrapper;
pub use config::ServerConfig;
pub use router::GotchaRouter;
pub use crate::builder::EmptyConfig;
pub use crate::builder::EmptyState;
pub use crate::builder::Gotcha;
pub use crate::config::GotchaConfigLoader;
pub use crate::error::GotchaError;
pub use crate::error::GotchaResult;
pub use crate::message::Message;
pub use crate::message::Messager;
pub use crate::openapi::Operable;openapi
pub use crate::params::Cookie;
pub use crate::params::CookieParam;
pub use crate::params::Header;
pub use crate::params::HeaderParam;
pub use crate::params::ParamRejection;
pub use crate::validation::Valid;
pub use crate::validation::ValidRejection;
pub use task::TaskScheduler;task
pub use axum;
pub use inventory;
pub use tracing;
pub use oas;openapi
pub use axum_extra::headers;
pub use serde_json;

ModulesΒ§

builder
Gotcha Builder API
config
Simplified configuration system built on mofa
error
The unified error type for the Gotcha framework.
layers
Middleware layers re-exported from tower-http.
message
Message Module
middleware
Writing custom middleware β€” middleware::from_fn and friends. Utilities for writing middleware
openapiopenapi
OpenAPI Module
params
Typed header and cookie parameters.
prelude
Gotcha Prelude
prometheusprometheus
Prometheus metrics, re-exported from axum-prometheus.
router
The router that tracks OpenAPI operations alongside axum routes.
sse
Server-sent events. Server-Sent Events (SSE) responses.
tasktask
Task Module
validation
Request-body validation via the validator crate.
ws
WebSocket upgrade and the socket itself. The frame type stays behind ws::Message, since Message is already the message-system trait. Handle WebSocket connections.

MacrosΒ§

handler
Defines an async handler function with less ceremony.
json_response
Builds a Json response from a serde_json::json! literal.
quick_server
Starts a server on the given address with the given routes, for examples and prototypes.

StructsΒ§

EnhancedSchemaopenapi
A schema plus whether the value it describes is required where it appears.
Event
Server-sent events. Server-sent event
Extension
Extractor and response for extensions.
Form
URL encoded extractor and response.
GotchaContext
The axum state the framework injects: the loaded configuration plus the application state.
Json
JSON Extractor / Response.
KeepAlive
Server-sent events. Configure the interval between keep-alive messages, the content of each message, and the associated stream.
Lazy
A value which is initialized on the first access.
MatchedPath
The request path as matched by the router (/users/{id}) and the URI before any nesting rewrote it. Both need axum features that this crate turns on. Access the path in the router that matches the request.
Multipart
Extractor that parses multipart/form-data requests (commonly used with file uploads).
OriginalUri
The request path as matched by the router (/users/{id}) and the URI before any nesting rewrote it. Both need axum features that this crate turns on. Extractor that gets the original request URI regardless of nesting.
Path
Extractor that will get captures from the URL and parse them using serde.
Query
Extractor that deserializes query strings into some type.
ServeDirstatic_files
Service that serves files from a given directory and all its sub directories.
ServeFilestatic_files
Service that serves a file.
Sse
Server-sent events. An SSE response
State
Extractor for state.
TypedHeader
axum’s typed-header extractor and the header types it works with. TypedHeader<T> documents itself as an OpenAPI header parameter (the name comes from headers::Header). Extractor and response that works with typed header values from headers.
WebSocket
WebSocket upgrade and the socket itself. The frame type stays behind ws::Message, since Message is already the message-system trait. A stream of WebSocket messages.
WebSocketUpgrade
WebSocket upgrade and the socket itself. The frame type stays behind ws::Message, since Message is already the message-system trait. Extractor for establishing WebSocket connections.

EnumsΒ§

Either
The enum Either with variants Left and Right is a general purpose sum type with two cases.

TraitsΒ§

GotchaApp
The trait API: implement it to describe an application, then call run().
GotchaConfig
Marker trait bundling the bounds every Gotcha application Config must meet.
ParameterProvideropenapi
ParameterProvider is a trait that defines the value which can be used as a parameter.
Responder
Trait for generating responses.
Responsibleopenapi
Maps a handler’s return type to the operation’s OpenAPI responses.
Schematicopenapi
Schematic is a trait that defines the schema of a type.
Validate
Derive and trait for request validation (re-exported from the validator crate). Use with the Valid extractor. This is the original trait that was implemented by deriving Validate. It will still be implemented for struct validations that don’t take custom arguments. The call is being forwarded to the ValidateArgs<'v_a> trait.

FunctionsΒ§

delete
Route DELETE requests to the given handler.
get
Route GET requests to the given handler.
patch
Route PATCH requests to the given handler.
post
Route POST requests to the given handler.
put
Route PUT requests to the given handler.

Attribute MacrosΒ§

apiopenapi
Generates OpenAPI documentation for route handler functions.
async_trait
config
Attribute macro that makes a struct usable as State<T> in handlers by generating a FromRef<GotchaContext<T, C>> impl. See GotchaContext. Marks a struct as a Gotcha application state so it can be extracted directly with axum’s State<T> in handlers.
debug_handler
Generates better error messages when applied to handler functions.
state
Attribute macro that makes a struct usable as State<T> in handlers by generating a FromRef<GotchaContext<T, C>> impl. See GotchaContext.

Derive MacrosΒ§

Schematicopenapi
Derives OpenAPI schema generation for structs and enums.
Validate
Derive and trait for request validation (re-exported from the validator crate). Use with the Valid extractor.