Middleware OverviewΒΆ
Cello's middleware system is implemented entirely in Rust as an async chain. Each incoming request passes through the middleware stack before reaching your handler, and the response passes back through in reverse order. Because the middleware runs in Rust, there is virtually zero Python overhead per request.
How Middleware WorksΒΆ
Request
β
βΌ
ββββββββββββββββββββββββββββ
β Rate Limiting (Rust) β β May short-circuit with 429
ββββββββββββββββββββββββββββ€
β Security Headers (Rust) β β Adds CSP, HSTS, etc.
ββββββββββββββββββββββββββββ€
β CORS (Rust) β β Handles preflight, adds headers
ββββββββββββββββββββββββββββ€
β CSRF Protection (Rust) β β Validates tokens on POST/PUT/DELETE
ββββββββββββββββββββββββββββ€
β Authentication (Rust) β β JWT/Basic/API Key validation
ββββββββββββββββββββββββββββ€
β Logging (Rust) β β Records request/response info
ββββββββββββββββββββββββββββ€
β Compression (Rust) β β Compresses response body
ββββββββββββββββββββββββββββ€
β Caching (Rust) β β Returns cached response if available
ββββββββββββββββββββββββββββ€
β Circuit Breaker (Rust) β β Opens circuit on repeated failures
ββββββββββββββββββββββββββββ€
β Your Handler (Python) β β Business logic
ββββββββββββββββββββββββββββ
β
βΌ
Response
Each middleware can:
- Continue -- pass the request to the next middleware in the chain.
- Stop -- short-circuit and return a response immediately (e.g., rate limit exceeded).
- Error -- return an error response.
Built-in MiddlewareΒΆ
| Middleware | Enable Method | Description | Version |
|---|---|---|---|
| CORS | app.enable_cors() | Cross-Origin Resource Sharing | v0.2.0 |
| Logging | app.enable_logging() | Request/response logging | v0.2.0 |
| Compression | app.enable_compression() | Gzip response compression | v0.2.0 |
| Rate Limiting | app.enable_rate_limit() | Token bucket / sliding window | v0.4.0 |
| JWT Auth | app.use(JwtAuth(...)) | JSON Web Token authentication | v0.4.0 |
| Basic Auth | app.use(BasicAuth(...)) | HTTP Basic authentication | v0.4.0 |
| API Key Auth | app.use(ApiKeyAuth(...)) | API key header validation | v0.4.0 |
| Sessions | app.enable_sessions() | Secure cookie sessions | v0.4.0 |
| CSRF | app.enable_csrf() | Cross-Site Request Forgery protection | v0.4.0 |
| Security Headers | app.enable_security_headers() | CSP, HSTS, X-Frame-Options | v0.4.0 |
| Body Limit | automatic | Request size validation | v0.4.0 |
| Request ID | automatic | UUID-based request tracing | v0.4.0 |
| ETag | automatic | Conditional request support | v0.4.0 |
| Static Files | app.enable_static_files() | Static file serving | v0.4.0 |
| Prometheus | app.enable_prometheus() | Metrics collection | v0.5.0 |
| Caching | app.enable_caching() | Smart caching with TTL | v0.6.0 |
| Circuit Breaker | app.enable_circuit_breaker() | Fault tolerance | v0.6.0 |
Enabling MiddlewareΒΆ
Zero-Configuration DefaultsΒΆ
Most middleware works with sensible defaults:
from cello import App
app = App()
app.enable_cors() # Allow all origins
app.enable_logging() # Log requests to stdout
app.enable_compression() # Gzip responses > 1KB
Customized ConfigurationΒΆ
Every middleware accepts configuration parameters:
from cello import App, RateLimitConfig, SessionConfig
app = App()
# CORS with specific origins
app.enable_cors(origins=["https://example.com", "https://app.example.com"])
# Compression with custom minimum size
app.enable_compression(min_size=512)
# Rate limiting with token bucket algorithm
app.enable_rate_limit(RateLimitConfig.token_bucket(
requests=100,
window=60
))
# Sessions with full configuration
app.enable_sessions(SessionConfig(
secret=b"session-secret-minimum-32-bytes-long",
max_age=86400
))
Middleware OrderingΒΆ
Middleware runs in the order it is registered. The recommended order is:
- Rate Limiting -- reject abusive requests early
- Security Headers -- always set headers
- CORS -- handle preflight before auth
- CSRF -- validate tokens before processing
- Authentication -- validate credentials
- Logging -- log after auth (includes user info)
- Compression -- compress at the end
- Caching -- cache final responses
app = App()
# Register in recommended order
app.enable_rate_limit(RateLimitConfig.token_bucket(requests=100, window=60))
app.enable_security_headers()
app.enable_cors()
app.enable_csrf()
app.use(JwtAuth(jwt_config))
app.enable_logging()
app.enable_compression()
app.enable_caching(ttl=300)
Order Matters
Placing rate limiting first means abusive requests are rejected before any authentication or business logic runs, saving server resources.
Using app.use()ΒΆ
For middleware that requires configuration objects (like authentication), use app.use():
from cello.middleware import JwtAuth, JwtConfig, BasicAuth, ApiKeyAuth
# JWT authentication
jwt_config = JwtConfig(secret=b"your-secret-key-minimum-32-bytes-long")
app.use(JwtAuth(jwt_config))
# Basic authentication
app.use(BasicAuth(verify_credentials))
# API Key authentication
app.use(ApiKeyAuth(keys={"key1": "service-a"}, header="X-API-Key"))
Async Handler CompatibilityΒΆ
All Python-side middleware wrappers -- including the @cache decorator, guard wrappers, and Pydantic validation -- fully support async handlers. Each wrapper uses inspect.iscoroutinefunction() to detect async handlers at decoration time and generates the appropriate sync or async wrapper. This means you can freely use async def handlers with any combination of caching, guards, and validation without encountering unawaited coroutine issues.
from cello import App, cache
from cello.guards import RoleGuard
from pydantic import BaseModel
class Item(BaseModel):
name: str
price: float
# Guards + validation + cache all work with async handlers
@app.get("/items", guards=[RoleGuard(["viewer"])])
@cache(ttl=60, tags=["items"])
async def list_items(request):
return {"items": await db.fetch_all("SELECT * FROM items")}
@app.post("/items", guards=[RoleGuard(["editor"])])
async def create_item(request, item: Item):
result = await db.insert(item.model_dump())
return {"id": result["id"]}
Middleware PerformanceΒΆ
All middleware is implemented in Rust with zero-allocation fast paths:
| Middleware | Overhead per Request | Notes |
|---|---|---|
| CORS | ~50ns | Header checks only |
| Rate Limiting | ~100ns | Lock-free counters |
| JWT Validation | ~50us | Constant-time comparison |
| Security Headers | ~20ns | Static header insertion |
| Compression | ~1us/KB | Native gzip |
| Logging | ~200ns | Async I/O |
| Caching | ~100ns | DashMap lookup |
| Circuit Breaker | ~50ns | Atomic state check |
Next StepsΒΆ
- CORS - Cross-Origin Resource Sharing
- Rate Limiting - Request throttling
- Caching - Smart response caching
- Circuit Breaker - Fault tolerance
- Compression - Response compression
- Logging - Request logging