Skip to content
>_devvkit
$devvkit learn --librarie actix-web-guide

Actix Web Guide

[rust][http][async][high-performance]
Rust
Install
cargo add actix-web actix-rt
# or: add to Cargo.toml manually

Actix Web is one of the fastest web frameworks in any language. It uses an actor-based runtime (actix-rt) and provides a full set of features: routing, middleware, WebSockets, multipart uploads, SSL, and streaming.

The framework uses extractors to pull data from requests (Path, Query, Json, Form, Data for app state). Handlers return types implementing Responder (HttpResponse, Json, web::Redirect, etc.).

Actix Web has excellent WebSocket support via actix-web-actors. The middleware system uses the `Service` trait with wrap_fn for simple cases or custom middleware structs for complex logic.

Setup

Basic serverHello world.
use actix_web::{get, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    HttpResponse::Ok().body("Hello world!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| App::new().service(hello))
        .bind(("127.0.0.1", 8080))?
        .run()
        .await
}

Routing

Route with paramsPath and query parameters.
use actix_web::{get, web, HttpResponse};

#[get("/users/{id}")]
async fn get_user(path: web::Path<u32>, query: web::Query<Search>) -> HttpResponse {
    let user_id = path.into_inner();
    HttpResponse::Ok().json(SearchResult { id: user_id, query: query.into_inner() })
}
JSON responseReturn JSON.
use serde::Serialize;

#[derive(Serialize)]
struct User { id: u32, name: String }

#[get("/users")]
async fn list_users() -> HttpResponse {
    let users = vec![
        User { id: 1, name: "Alice".into() },
        User { id: 2, name: "Bob".into() },
    ];
    HttpResponse::Ok().json(users)
}

Extractors

Shared stateApplication state.
use std::sync::Mutex;

struct AppState { counter: Mutex<u32> }

#[get("/count")]
async fn get_count(data: web::Data<AppState>) -> HttpResponse {
    let mut count = data.counter.lock().unwrap();
    *count += 1;
    HttpResponse::Ok().body(format!("Count: {}", count))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let state = web::Data::new(AppState { counter: Mutex::new(0) });
    HttpServer::new(move || App::new().app_data(state.clone()).service(get_count))
        .bind(("127.0.0.1", 8080))?
        .run().await
}

Middleware

MiddlewareSimple wrap middleware.
use actix_web::middleware::from_fn;

async fn my_middleware(
    req: actix_web::dev::ServiceRequest,
    next: actix_web::dev::ServiceResponse,
) -> Result<actix_web::dev::ServiceResponse, actix_web::Error> {
    let start = std::time::Instant::now();
    let res = next.call(req).await?;
    log::info!("Request took {:?}", start.elapsed());
    Ok(res)
}

App::new().wrap(from_fn(my_middleware))

WebSockets

WebSocketWebSocket endpoint.
use actix_web::{web, HttpRequest, HttpResponse};
use actix_web_actors::ws;

async fn ws_handler(req: HttpRequest, stream: web::Payload) -> Result<HttpResponse, actix_web::Error> {
    ws::start(MyWebSocket, &req, stream)
}

App::new().route("/ws/", web::get().to(ws_handler))

Testing

Test integrationIntegration test.
#[cfg(test)]
mod tests {
    use actix_web::{test, App, HttpResponse};

    #[actix_web::test]
    async fn test_hello() {
        let app = test::init_service(App::new().route("/", web::get().to(hello))).await;
        let req = test::TestRequest::get().uri("/").to_request();
        let resp = test::call_service(&app, req).await;
        assert!(resp.status().is_success());
    }
}