Expand description
Β§Gotcha
An enhanced web framework built on top of Axum, providing additional features and conveniences for building robust web applications in Rust.
Β§β¨ 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 documentationprometheus- Metrics collection and expositioncors- Cross-Origin Resource Sharing supportstatic_files- Static file serving capabilitiestask- 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 = 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.
Β§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
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
python3 test-feature-matrix.py - Submit a pull request
Β§π License
This project is licensed under the MIT License - see the LICENSE file for details.
Β§π Related Projects
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;openapipub 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;taskpub use axum;pub use inventory;pub use tracing;pub use oas;openapipub 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_fnand friends. Utilities for writing middleware - openapi
openapi - OpenAPI Module
- params
- Typed header and cookie parameters.
- prelude
- Gotcha Prelude
- prometheus
prometheus - 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.
- task
task - Task Module
- validation
- Request-body validation via the
validatorcrate. - ws
- WebSocket upgrade and the socket itself. The frame type stays behind
ws::Message, sinceMessageis already the message-system trait. Handle WebSocket connections.
MacrosΒ§
- handler
- Defines an async handler function with less ceremony.
- json_
response - Builds a
Jsonresponse from aserde_json::json!literal. - quick_
server - Starts a server on the given address with the given routes, for examples and prototypes.
StructsΒ§
- Enhanced
Schema openapi - 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.
- Gotcha
Context - The axum state the framework injects: the loaded configuration plus the application state.
- Json
- JSON Extractor / Response.
- Keep
Alive - 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.
- Matched
Path - 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-datarequests (commonly used with file uploads). - Original
Uri - 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.
- Serve
Dir static_files - Service that serves files from a given directory and all its sub directories.
- Serve
File static_files - Service that serves a file.
- Sse
- Server-sent events. An SSE response
- State
- Extractor for state.
- Typed
Header - axumβs typed-header extractor and the header types it works with.
TypedHeader<T>documents itself as an OpenAPI header parameter (the name comes fromheaders::Header). Extractor and response that works with typed header values fromheaders. - WebSocket
- WebSocket upgrade and the socket itself. The frame type stays behind
ws::Message, sinceMessageis already the message-system trait. A stream of WebSocket messages. - WebSocket
Upgrade - WebSocket upgrade and the socket itself. The frame type stays behind
ws::Message, sinceMessageis already the message-system trait. Extractor for establishing WebSocket connections.
EnumsΒ§
- Either
- The enum
Eitherwith variantsLeftandRightis a general purpose sum type with two cases.
TraitsΒ§
- Gotcha
App - The trait API: implement it to describe an application, then call
run(). - Gotcha
Config - Marker trait bundling the bounds every Gotcha application
Configmust meet. - Parameter
Provider openapi - ParameterProvider is a trait that defines the value which can be used as a parameter.
- Responder
- Trait for generating responses.
- Responsible
openapi - Maps a handlerβs return type to the operationβs OpenAPI responses.
- Schematic
openapi - Schematic is a trait that defines the schema of a type.
- Validate
- Derive and trait for request validation (re-exported from the
validatorcrate). Use with theValidextractor. This is the original trait that was implemented by derivingValidate. It will still be implemented for struct validations that donβt take custom arguments. The call is being forwarded to theValidateArgs<'v_a>trait.
FunctionsΒ§
- delete
- Route
DELETErequests to the given handler. - get
- Route
GETrequests to the given handler. - patch
- Route
PATCHrequests to the given handler. - post
- Route
POSTrequests to the given handler. - put
- Route
PUTrequests to the given handler.
Attribute MacrosΒ§
- api
openapi - Generates OpenAPI documentation for route handler functions.
- async_
trait - config
- Attribute macro that makes a struct usable as
State<T>in handlers by generating aFromRef<GotchaContext<T, C>>impl. SeeGotchaContext. Marks a struct as a Gotcha application state so it can be extracted directly with axumβsState<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 aFromRef<GotchaContext<T, C>>impl. SeeGotchaContext.