SKILL.md
Web Domain
Layer 3: Domain Constraints
Domain Constraints → Design Implications
Domain Rule
Design Constraint
Rust Implication
Stateless HTTP
No request-local globals
State in extractors
Concurrency
Handle many connections
Async, Send + Sync
Latency SLA
Fast response
Efficient ownership
Security
Input validation
Type-safe extractors
Observability
Request tracing
tracing + tower layers
Critical Constraints
Async by Default
RULE: Web handlers must not block
WHY: Block one task = block many requests
RUST: async/await, spawn_blocking for CPU work
State Management
RULE: Shared state must be thread-safe
WHY: Handlers run on any thread
RUST: Arc<T>, Arc<RwLock<T>> for mutable
Request Lifecycle
RULE: Resources live only for request duration
WHY: Memory management, no leaks
RUST: Extractors, proper ownership
Trace Down ↓
From constraints to design (Layer 2):
"Need shared application state"
↓ m07-concurrency: Use Arc for thread-safe sharing
↓ m02-resource: Arc<RwLock<T>> for mutable state
"Need request validation"
↓ m05-type-driven: Validated extractors
↓ m06-error-handling: IntoResponse for errors
"Need middleware stack"
↓ m12-lifecycle: Tower layers
↓ m04-zero-cost: Trait-based composition
Framework Comparison
Framework
Style
Best For
axum
Functional, tower
Modern APIs
actix-web
Actor-based
High performance
warp
Filter composition
Composable APIs
rocket
Macro-driven
Rapid development
Key Crates
Purpose
Crate
HTTP server
axum, actix-web
HTTP client
reqwest
JSON
serde_json
Auth/JWT
jsonwebtoken
Session
tower-sessions
Database
sqlx, diesel
Middleware
tower
Design Patterns
Pattern
Purpose
Implementation
Extractors
Request parsing
State(db), Json(payload)
Error response
Unified errors
impl IntoResponse
Middleware
Cross-cutting
Tower layers
Shared state
App config
Arc<AppState>
Code Pattern: Axum Handler
async fn handler(
State(db): State<Arc<DbPool>>,
Json(payload): Json<CreateUser>,
) -> Result<Json<User>, AppError> {
let user = db.create_user(&payload).await?;
Ok(Json(user))
}
// Error handling
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
Self::NotFound => (StatusCode::NOT_FOUND, "Not found"),
Self::Internal(_) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error"),
};
(status, Json(json!({"error": message}))).into_response()
}
}
Common Mistakes
Mistake
Domain Violation
Fix
Blocking in handler
Latency spike
spawn_blocking
Rc in state
Not Send + Sync
Use Arc
No validation
Security risk
Type-safe extractors
No error response
Bad UX
IntoResponse impl
Trace to Layer 1
Constraint
Layer 2 Pattern
Layer 1 Implementation
Async handlers
Async/await
tokio runtime
Thread-safe state
Shared state
Arc, Arc<RwLock>
Request lifecycle
Extractors
Ownership via From
Middleware
Tower layers
Trait-based composition
Related Skills
When
See
Async patterns
m07-concurrency
State management
m02-resource
Error handling
m06-error-handling
Middleware design
m12-lifecycle