We use cookies to understand how the site is used and to display ads. Analytics and advertising only run after you accept. You can change your choice anytime. Privacy policy

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 server· Hello 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 params· Path 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 response· Return 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 state· Application 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

Middleware· Simple 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

WebSocket· WebSocket 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 integration· Integration 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());
    }
}