Actix Web: The Ultimate Guide (2023)

This is the most comprehensive tutorial on the Actix Web framework online.

In this Actix Web tutorial, you will learn Actix Web from scratch to an advanced level.

You will learn how to build and deploy your first Actix Web app.

Actix Web: The Ultimate Guide (2023)

Chapter 1: Complete Actix Overview

This Actix tutorial will teach you everything you need to know about building scaling backend systems with Rust and the Rust ecosystem.

In recent years, web development has shifted towards more performant and reliable technologies.

One language that has gained traction for backend web development is Rust, known for its safety and speed.

Name

custom

Title

Rust Essentials

URL

https://masteringbackend.com/books/rust-essentials

description

This is the most comprehensive guide to the Rust Programming Language online. I’ll programmatically introduce you to Rust Programming Language in this Rust essential.

This article will explore Rust's Actix web framework, a powerful and efficient tool for building web applications. We'll dive into the basics of Actix, its key features, and how to get started with backend web development using this framework.

What is Actix Web

Actix Web is a high-performance, actor-based web framework for Rust. It is built on the Actix actor framework, which leverages Rust's concurrency and safety features. Actix Web is known for its exceptional speed, making it an excellent choice for building web applications that require low latency and high throughput.

Key Features of Actix Web

Actix web offers a wide range of features that make it a compelling choice for backend web development:

  1. Asynchronous and Non-blocking: Actix web fully embraces Rust's async/await system, allowing developers to write non-blocking code that efficiently handles many concurrent requests.

  2. Actor Model: Actix web is built on top of the Actix actor framework, which provides a powerful way to manage application state and handle concurrency. Actors are lightweight, isolated units of execution that can communicate with each other through messages.

  3. Middleware Support: Actix web supports middleware, which allows developers to add reusable functionality to the request/response handling pipeline. This makes implementing features like authentication, logging, and compression easy.

  4. WebSocket Support: Actix Web provides built-in support for WebSockets, enabling real-time communication between clients and the server.

  5. Extensible: Actix web is highly extensible, and developers can create custom components, middleware, and error handlers to tailor the framework to their specific needs.

  6. Testing: Actix web includes a robust testing framework that simplifies unit and integration testing of web applications.

  7. HTTP/2 and TLS: The framework supports HTTP/2 and TLS, ensuring secure and efficient client communication.

Now that we have an overview of Actix web's features, let's build a basic web application using this framework.

Chapter 2: Getting Started with Actix Web

In this chapter, we will start building out our first application using Rust Actix to understand the underlying patterns of the framework.

Before you build web applications with Actix Web, you'll need to set up a Rust development environment. If you need to, follow the official Rust installation instructions at https://www.rust-lang.org/learn/get-started.

Once Rust is installed, you can create a new Rust project and add Actix web as a dependency in your Cargo.toml file:

[dependencies]
actix-web = "4.4.0"

Now, let's create a simple Actix web application step by step.

Creating a Basic Actix Web Application

Create a new Rust project with the following command:

cargo new actix_web_demo
cd actix_web_demo

Next, open your project's Cargo.toml file and add Actix web as a dependency, as mentioned earlier. Then, your Cargo.toml should look like this:

[dependencies]
actix-web = "4.4.0"

Creating the Application Entry Point

In Rust, the entry point of your application is the main function. Create a main.rs file in your project's root directory and define the main function:

use actix_web::{get, App, HttpServer, Responder};

#[get("/")]
async fn hello() -> impl Responder {
    "Hello, Actix web!"
}

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

In this code:

  • We import necessary items from Actix web.

  • We define a simple asynchronous function hello that responds with the string "Hello, Actix web!" when the root URL ("/") is accessed.

  • We create the main function which sets up an Actix web server. It uses HttpServer::new to configure the server and App::new to create an application with the hello route.

  • Finally, we bind the server to the address "127.0.0.1:8080" and run it asynchronously.

Running the Application

To run your Actix web application, use the following command from your project's root directory:

cargo run

This will start the Actix web server, and you'll see an output indicating that the server is running on 127.0.0.1:8080.

Accessing the Application

Open your web browser and navigate to http://localhost:8080. You should see the message "Hello, Actix web!" displayed in your browser. Alternatively, you can use the cURL tool to access the route from the terminal:

actix result

Congratulations! You've created a basic Actix web application.

Chapter 3: Routing and Request Handling

This chapter will explore defining routes and handling different requests in Actix web.

Actix Web provides a flexible routing system that allows you to define routes and handle different HTTP requests (e.g., GET, POST, PUT, DELETE). Let's explore how to define routes and handle different requests in Actix web.

Defining Routes

In Actix web, you define routes using attributes like get, post, put, and delete on functions. Here's an example of defining multiple routes:

use actix_web::{get, post, put, delete, web, App, HttpResponse, HttpServer, Responder};

#[get("/")]
async fn index() -> impl Responder {
    HttpResponse::Ok().body("Welcome to the index page!")
}

#[post("/create")]
async fn create() -> impl Responder {
    HttpResponse::Created().body("Resource created successfully!")
}

#[put("/update")]
async fn update() -> impl Responder {
    HttpResponse::Ok().body("Resource updated successfully!")
}

#[delete("/delete")]
async fn delete() -> impl Responder {
    HttpResponse::NoContent().finish()
}

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

In this code, we've defined four routes:

  • index responds to GET requests to the root ("/") URL.

  • create responses to POST requests to the "/create" URL.

  • update responds to PUT requests to the "/update" URL.

  • delete responds to DELETE requests to the "/delete" URL.

Each route returns an appropriate HTTP response using Actix web's HttpResponse type.

Path Parameters

You can also define routes with dynamic path parameters using curly braces. For example:

use actix_web::{
    get, web, App, HttpResponse, HttpServer, Responder
};

#[get("/user/{id}/{name}")]
async fn user_info(info: web::Path<(u32, String)>) -> impl Responder {
    let (id, name) = info.into_inner();
    HttpResponse::Ok().body(format!("User ID: {}, Name: {}", id, name))
}

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

In this example, the user_info route takes two path parameters: id (a u32) and name (a String). When you access a URL like "/user/123/john", the values of id and name are extracted from the URL, and the route responds with "User ID: 123, Name: john".

Untitled (41).png

Request Data

To handle data sent in HTTP requests (e.g., JSON data in a POST request), you can use Actix web's data extraction features. Here's an example of parsing JSON data from a POST request:

use actix_web::{
    post, web, App, HttpResponse, HttpServer, Responder
};

#[derive(Debug, serde::Deserialize, serde::Serialize)]
struct User {
    username: String,
    email: String,
}

#[post("/create_user")]
async fn create_user(user: web::Json<User>) -> impl Responder {
    let new_user = user.into_inner();
    // Process the new user data (e.g., save it to a database)
    HttpResponse::Created().json(new_user)
}

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

In this code:

  • We define a User struct representing the JSON data we expect in the POST request.

  • The create_user route takes a type web::Json<User> parameter, automatically parsing JSON data from the request body into a User struct.

  • We can then process the parsed user data and return an appropriate HTTP response.

Chapter 4: Advanced Actix Web Guide

In the advanced Actix Web Guide, what you will learn includes:

  • Middlewares in Actix

  • Authentication and Authorization with Actix Web

  • Working with Databases in Actix Web

  • Advanced Error Handling

  • Implementing WebSockets in Actix Web

  • Milestone Project: Real-Time Chat Application

Don't Stop Learning

Continue reading the Middleware in Actix web for $6.99 only or Get instant access to all current and upcoming courses and content through subscription.

Middleware in Actix web allows you to add functionality to the request/response handling pipeline. You can use middleware for tasks like authentication, logging, and compression. Actix Web provides built-in middleware and allows you to create custom middleware.

Using Built-in Middleware

To use built-in middleware, you can chain them together in your application setup. Here's an example of adding middleware for request logging and compression:

use actix_web::{middleware, App, HttpResponse, HttpServer, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok().body("Welcome to the index page!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default()) // Request logging middleware
            .wrap(middleware::Compress::default()) // Response compression middleware
            .service(index)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

In this example, we use middleware::Logger::default() to add request logging middleware and middleware::Compress::default() to add response compression middleware. The .wrap() method adds these middleware components to the application.

Creating Custom Middleware

To create custom middleware, you define a function that takes an actix_web::dev::Service and returns a new Service. Here's a simple example of custom middleware that adds a custom header to the response:

use actix_service::Service;
use actix_web::{error, middleware, App, HttpResponse, HttpServer, Responder};

async fn index() -> impl Responder {
    HttpResponse::Ok().body("Welcome to the index page!")
}

async fn custom_middleware<S>(
    req: actix_web::dev::ServiceRequest,
    srv: S,
) -> Result<actix_web::dev::ServiceResponse, actix_web::Error>
where
    S: actix_service::Service<
        Request = actix_web::dev::ServiceRequest,
        Response = actix_web::dev::ServiceResponse,
        Error = actix_web::Error,
    >,
{
    // Call the inner service to get the response
    let mut response = srv.call(req).await?;

    // Add a custom header to the response
    response.headers_mut().insert(
        actix_web::http::header::HeaderName::from_static("X-Custom-Header"),
        actix_web::http::HeaderValue::from_static("MyCustomValue"),
    );

    Ok(response)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(middleware::Logger::default())
            .wrap_fn(custom_middleware) // Add custom middleware
            .service(index)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

In this example, we define a custom middleware function custom_middleware, that takes a request and an inner service (srv). We call the inner service to get the response and add a custom header before returning it. The custom middleware is added to the application using .wrap_fn().

Conclusion: Actix Web

In this Actix Web tutorial, we have looked at Actix Web's nitty-gritty and created a Real-Time Chat application to demonstrate the knowledge we have gained so far.

Now, it’s your turn to practice everything you have learned until you master them by building a real-world application.

Let me know what you’ve learned from this Actix Web tutorial and what you will build.

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board

Backend Tips, Every week

Backend Tips, Every week