Actix-web Core Knowledge
Full Reference: See advanced.md for custom timing middleware, authentication middleware, custom error types, WebSocket actors, and graceful shutdown patterns.
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: actix-web for comprehensive documentation.
Basic Setup
Cargo.toml
[dependencies] actix-web = "4" actix-rt = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] }
use actix_web::{web, App, HttpServer, HttpResponse, Responder};
async fn hello() -> impl Responder { HttpResponse::Ok().body("Hello, World!") }
#[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/", web::get().to(hello)) }) .bind("127.0.0.1:8080")? .run() .await }
Route Macros
use actix_web::{get, post, web, HttpResponse, Responder};
#[get("/users/{id}")] async fn get_user(path: web::Path<u32>) -> impl Responder { let id = path.into_inner(); HttpResponse::Ok().json(serde_json::json!({ "id": id })) }
#[post("/users")] async fn create_user(body: web::Json<CreateUser>) -> impl Responder { HttpResponse::Created().json(body.into_inner()) }
// Register with App App::new() .service(get_user) .service(create_user)
Extractors
Extractor Purpose
web::Path<T>
URL path parameters
web::Query<T>
Query string
web::Json<T>
JSON body
web::Form<T>
Form data
web::Data<T>
Application state
Application State
struct AppState { db_pool: Pool<Postgres>, }
#[get("/users")] async fn list_users(data: web::Data<AppState>) -> impl Responder { let users = sqlx::query_as!(User, "SELECT * FROM users") .fetch_all(&data.db_pool) .await?; HttpResponse::Ok().json(users) }
HttpServer::new(move || { App::new() .app_data(web::Data::new(state.clone())) .service(list_users) })
Built-in Middleware
use actix_web::middleware::{Logger, Compress, NormalizePath};
App::new() .wrap(Logger::default()) .wrap(Compress::default()) .wrap(NormalizePath::trim())
Health Checks
#[get("/health")] async fn health() -> impl Responder { HttpResponse::Ok().json(serde_json::json!({ "status": "healthy" })) }
#[get("/ready")] async fn ready(data: web::Data<AppState>) -> impl Responder { match data.db_pool.acquire().await { Ok() => HttpResponse::Ok().json(serde_json::json!({ "status": "ready", "database": "connected" })), Err() => HttpResponse::ServiceUnavailable().json(serde_json::json!({ "status": "not ready" })), } }
When NOT to Use This Skill
-
Axum projects - Axum is more ergonomic with Tower ecosystem
-
Rocket projects - Rocket has better compile-time guarantees
-
Warp projects - Warp uses filters for composition
-
Simple CLI tools - No web server needed
-
Embedded systems - Too heavy for resource-constrained devices
Anti-Patterns
Anti-Pattern Why It's Bad Solution
.clone() on every request Performance overhead Use web::Data<Arc<T>> for shared state
Blocking I/O in async handlers Blocks executor threads Use web::block() for blocking operations
Not using extractors Manual parsing is error-prone Use Json , Path , Query extractors
Missing #[actix_web::main]
Manual runtime setup Use macro for simple setup
Global mutable state Data races Use Mutex or RwLock with web::Data
No custom error types Generic error messages Implement ResponseError trait
Quick Troubleshooting
Problem Diagnosis Fix
"Cannot move out of borrowed content" Ownership issue Clone data or use web::Data<Arc<T>>
Handler not found (404) Route not registered Check .service() or .route() calls
JSON parsing fails Wrong content-type Ensure client sends Content-Type: application/json
Slow performance Blocking I/O Wrap blocking code in web::block()
WebSocket connection closes Missing ping/pong Implement heartbeat mechanism
State not accessible Not added to app Use .app_data() when building app
Checklist
-
CORS properly configured
-
Authentication middleware
-
Custom error handling
-
Request logging (Logger middleware)
-
Health/readiness endpoints
-
Graceful shutdown
-
Connection pooling for database
-
Input validation
-
Rate limiting (actix-ratelimit)
Reference Documentation
-
Extractors
-
Middleware