# ADR-002: Axum como Backend Framework **Status**: Accepted | Implemented **Date**: 2024-11-01 **Deciders**: Backend Architecture Team **Technical Story**: Selecting REST API framework with optimal async/middleware composition for Tokio ecosystem --- ## Decision Usar **Axum 0.8.6** como framework REST API (no Actix-Web, no Rocket) para exponer 40+ endpoints de VAPORA. --- ## Rationale 1. **Composable Middleware**: Tower ecosystem provides first-class composable middleware patterns 2. **Type-Safe Routing**: Router defined as strong types (not string-based paths) 3. **Tokio Ecosystem**: Built directly on Tokio (not abstraction layer), enabling precise async control 4. **Extractors**: Powerful extractor system (`Json`, `State`, `Path`, custom extractors) reduces boilerplate 5. **Performance**: Zero-copy response bodies, streaming support, minimal overhead --- ## Alternatives Considered ### ❌ Actix-Web - Mature framework with larger ecosystem - **Cons**: Actor model adds complexity, different async patterns than Tokio, harder to integrate with Tokio primitives ### ❌ Rocket - Developer-friendly API - **Cons**: Synchronous-first (async as afterthought), less composable, worse error handling ### ✅ Axum (CHOSEN) - Minimal abstraction over Tokio/Tower - **Pros**: Composable, type-safe, Tokio-native, growing ecosystem --- ## Trade-offs **Pros**: - ✅ Composable middleware (Tower trait-based) - ✅ Type-safe routing with strong types - ✅ Zero-cost abstractions, excellent performance - ✅ Perfect integration with Tokio async ecosystem - ✅ Streaming responses, WebSocket support built-in **Cons**: - ⚠️ Smaller ecosystem than Actix-Web - ⚠️ Steeper learning curve (requires understanding Tower traits) - ⚠️ Fewer third-party integrations available --- ## Implementation **Router Definition**: ```rust let app = Router::new() .route("/api/v1/projects", post(create_project).get(list_projects)) .route("/api/v1/projects/:id", get(get_project).put(update_project)) .route("/metrics", get(metrics_handler)) .layer(TraceLayer::new_for_http()) .layer(CorsLayer::permissive()) .layer(Extension(Arc::new(app_state))); let listener = TcpListener::bind("0.0.0.0:8001").await?; axum::serve(listener, app).await?; ``` **Key Files**: - `/crates/vapora-backend/src/main.rs:126-259` (router setup) - `/crates/vapora-backend/src/api/` (handlers) - `/crates/vapora-backend/Cargo.toml` (dependencies) --- ## Verification ```bash # Build backend cargo build -p vapora-backend # Test API endpoints cargo test -p vapora-backend -- --nocapture # Run server and check health cargo run -p vapora-backend & curl http://localhost:8001/health curl http://localhost:8001/metrics ``` **Expected**: 40+ endpoints accessible, health check responds 200 OK, metrics endpoint returns Prometheus format --- ## Consequences - All HTTP handling must use Axum extractors (learning curve for team) - Request/response types must be serializable (integration with serde) - Middleware stacking order matters (defensive against bugs) - Easy to add WebSocket support later (Axum has built-in support) --- ## References - [Axum Documentation](https://docs.rs/axum/) - `/crates/vapora-backend/src/main.rs` (router definition) - `/crates/vapora-backend/Cargo.toml` (Axum dependency) --- **Related ADRs**: ADR-001 (Workspace), ADR-008 (Tokio)