Architecture of a Runique Project

Middleware Stack

Slot Order

Runique applies middlewares in an optimal order using the slot system:

Incoming request
    ↓
1.  Extensions (slot 0)       → Inject Tera, Config, Engine
2.  TrustedProxies (slot 2)   → Real client IP
3.  Compression (slot 5)      → External compression
4.  CORS (slot 8)             → Before ErrorHandler (OPTIONS preflight)
5.  ErrorHandler (slot 10)    → Capture and render errors
6.  HostValidation (slot 15)  → Allowed Hosts validation
7.  Custom (slot 20+)         → Custom middlewares
8.  OpenRedirect (slot 25)    → Inspects 3xx responses
9.  Security Headers (slot 30) → HSTS, X-Frame-Options, etc.
10. CSP (slot 31)             → Content Security Policy
11. Cache (slot 40)           → No-cache in development
12. Session (slot 50)         → Session management
13. SessionUpgrade (slot 55)  → Reads/writes in session
14. Auth (slot 57)            → Loads CurrentUser from the session
15. CSRF (slot 60)            → CSRF protection
16. AntiBot (slot 65)         → Honeypot
    ↓
Handler (your code)
    ↓
Outgoing response (middlewares in reverse order)

Important: With Axum, the last .layer() applied is executed first. The Intelligent Builder manages this order automatically.


Dependency Injection

Via Axum Extensions, automatically injected by the Extensions middleware:

// Automatically registered by the builder:
// Extension(engine)  → Arc<RuniqueEngine>
// Extension(tera)    → Arc<Tera>
// Extension(config)  → Arc<RuniqueConfig>

// Accessible inside handlers via Request:
pub async fn handler(request: Request) -> AppResult<Response> {
    let db = request.engine.db.clone();
    let config = &request.engine.config;
    // ...
}