What is Actix Web?
Actix Web is one of the fastest web frameworks available in any language. Built on Rust's async ecosystem, it provides a powerful, pragmatic framework for building web services. It consistently tops the TechEmpower benchmarks.
Getting Started
// Cargo.toml
// [dependencies]
// actix-web = "4"
// 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!")
}
async fn health() -> impl Responder {
HttpResponse::Ok().json(serde_json::json!({
"status": "healthy",
"version": "1.0.0"
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
println!("Server running at http://localhost:8080");
HttpServer::new(|| {
App::new()
.route("/", web::get().to(hello))
.route("/health", web::get().to(health))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Routing and Extractors
use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct User {
id: u64,
name: String,
email: String,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[derive(Deserialize)]
struct Pagination {
page: Option,
per_page: Option,
}
// Path parameters
async fn get_user(path: web::Path) -> impl Responder {
let user_id = path.into_inner();
HttpResponse::Ok().json(User {
id: user_id,
name: "Alice".into(),
email: "alice@example.com".into(),
})
}
// JSON body
async fn create_user(body: web::Json) -> impl Responder {
let user = User {
id: 1,
name: body.name.clone(),
email: body.email.clone(),
};
HttpResponse::Created().json(user)
}
// Query parameters
async fn list_users(query: web::Query) -> impl Responder {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(20);
HttpResponse::Ok().json(serde_json::json!({
"page": page,
"per_page": per_page,
"users": []
}))
}
// Configure routes
fn config(cfg: &mut web::ServiceConfig) {
cfg.service(
web::scope("/api/v1")
.route("/users", web::get().to(list_users))
.route("/users", web::post().to(create_user))
.route("/users/{id}", web::get().to(get_user))
);
}
Middleware and Application State
use actix_web::{web, App, HttpServer, middleware};
use std::sync::Mutex;
// Application state
struct AppState {
request_count: Mutex,
db_pool: DatabasePool,
}
async fn with_state(data: web::Data) -> impl actix_web::Responder {
let mut count = data.request_count.lock().unwrap();
*count += 1;
actix_web::HttpResponse::Ok().json(serde_json::json!({
"request_number": *count
}))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Initialize shared state
let state = web::Data::new(AppState {
request_count: Mutex::new(0),
db_pool: DatabasePool::new(),
});
HttpServer::new(move || {
// CORS middleware
let cors = actix_cors::Cors::default()
.allow_any_origin()
.allow_any_method()
.allow_any_header();
App::new()
.app_data(state.clone())
.wrap(cors)
.wrap(middleware::Logger::default()) // Request logging
.wrap(middleware::Compress::default()) // Gzip compression
.route("/count", actix_web::web::get().to(with_state))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
struct DatabasePool;
impl DatabasePool { fn new() -> Self { DatabasePool } }
Error Handling
use actix_web::{HttpResponse, ResponseError};
use std::fmt;
#[derive(Debug)]
enum ApiError {
NotFound(String),
BadRequest(String),
Internal(String),
}
impl fmt::Display for ApiError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ApiError::NotFound(msg) => write!(f, "Not Found: {msg}"),
ApiError::BadRequest(msg) => write!(f, "Bad Request: {msg}"),
ApiError::Internal(msg) => write!(f, "Internal Error: {msg}"),
}
}
}
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
let (status, message) = match self {
ApiError::NotFound(msg) => (
actix_web::http::StatusCode::NOT_FOUND, msg),
ApiError::BadRequest(msg) => (
actix_web::http::StatusCode::BAD_REQUEST, msg),
ApiError::Internal(msg) => (
actix_web::http::StatusCode::INTERNAL_SERVER_ERROR, msg),
};
HttpResponse::build(status).json(serde_json::json!({
"error": message
}))
}
}
async fn get_user_by_id(path: actix_web::web::Path) -> Result {
let id = path.into_inner();
if id == 0 {
return Err(ApiError::BadRequest("ID must be > 0".into()));
}
if id > 1000 {
return Err(ApiError::NotFound(format!("User {id} not found")));
}
Ok(HttpResponse::Ok().json(serde_json::json!({"id": id, "name": "Alice"})))
}
Key Takeaways
- ✅ Actix Web is one of the fastest web frameworks, with excellent async support
- ✅ Extractors (Path, Json, Query, Data) automatically parse request data
- ✅ Application state is shared via
web::Datawith thread-safe wrappers - ✅ Implement
ResponseErrorfor custom error types with proper HTTP responses - ✅ Built-in middleware handles logging, compression, and CORS