Skip to main content
Rust Programming
CHAPTER 26 Beginner

Web Development with Rust

Updated: May 18, 2026
5 min read

# CHAPTER 26

Web Development with Rust

1. Chapter Introduction

Rust is rapidly becoming the language of choice for building high-performance backend infrastructure. Frameworks like Actix Web, Axum, and Rocket routinely top the charts in the TechEmpower Web Framework Benchmarks, massively outperforming Node.js, Python, and even Go. In this chapter, we will build a modern REST API using Actix Web, handling HTTP routing, JSON serialization, and server initialization.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Install and configure Actix Web and Serde.
  • Start an asynchronous HTTP server.
  • Define routing endpoints (GET, POST).
  • Serialize Rust Structs into JSON responses.
  • Extract data from URL paths.

3. Setting Up the Project

We need two main dependencies: actix-web for the server, and serde for handling JSON. Update your Cargo.toml:
toml
1234
[dependencies]
actix-web = "4"
# Serde allows us to easily convert Structs to JSON and back
serde = { version = "1.0", features = ["derive"] } 

4. Creating a Basic HTTP Server

Starting a web server requires an Async runtime. Actix Web comes with its own runtime macro (#[actix_web::main]) built on top of Tokio.
rust
12345678910111213141516171819202122
use actix_web::{get, App, HttpResponse, HttpServer, Responder};

// 1. Define a Route using a Macro
#[get("/hello")]
async fn hello() -> impl Responder {
    // Return a 200 OK with a text body
    HttpResponse::Ok().body("Hello from the Rust Backend!")
}

// 2. Start the Server
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://localhost:8080/");

    HttpServer::new(|| {
        App::new()
            .service(hello) // Register the route
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

*Run this code, open http://localhost:8080/hello in your browser, and see the result!*

5. Returning JSON (The Magic of Serde)

REST APIs communicate using JSON. We don't want to format JSON strings manually. Instead, we use the serde crate to automatically serialize our Rust Structs.
rust
12345678910111213141516171819202122
use actix_web::{get, web, App, HttpServer, Responder};
use serde::Serialize;

// Derive Serialize so Actix knows how to turn this into JSON
#[derive(Serialize)]
struct User {
    id: u32,
    username: String,
    role: String,
}

#[get("/api/user")]
async fn get_user() -> impl Responder {
    let user = User {
        id: 101,
        username: String::from("alice_dev"),
        role: String::from("admin"),
    };

    // Return 200 OK, but encode the struct as JSON!
    web::Json(user) 
}

*Output when visiting /api/user:* {"id": 101, "username": "alice_dev", "role": "admin"}

6. Extracting URL Path Variables

Often, clients request specific data, like /api/users/101. We can extract the 101 directly from the URL.
rust
12345678910
// The {} denotes a path variable
#[get("/api/users/{user_id}")]
async fn get_user_by_id(path: web::Path<u32>) -> impl Responder {
    // Extract the variable from the path
    let user_id = path.into_inner();

    // In a real app, query the database using 'user_id' here!

    format!("You requested data for User ID: {}", user_id)
}

7. Handling POST Requests

To handle a POST request (e.g., a client sending a JSON payload to create a new user), we use #[post], derive Deserialize on our struct, and use web::Json as a parameter.
rust
12345678910111213141516
use serde::Deserialize;

#[derive(Deserialize)]
struct CreateUserRequest {
    username: String,
}

#[actix_web::post("/api/users")]
async fn create_user(req_body: web::Json<CreateUserRequest>) -> impl Responder {
    // We now have safe, typed access to the JSON payload!
    let new_name = &req_body.username;
    
    // Process and save to database...

    HttpResponse::Created().body(format!("Created user: {}", new_name))
}

8. Common Mistakes

  • Forgetting to Register Services: Writing a route function like async fn login() but forgetting to add .service(login) inside the HttpServer::new closure. The route will return a 404 Not Found.
  • Missing Serde Derivations: Trying to return web::Json(mystruct) without adding #[derive(Serialize)] to the struct. The compiler will panic, saying the trait bound is not satisfied.

9. Best Practices

  • Extract Application State: Actix Web allows you to inject shared state (like a Database Connection Pool from Chapter 25) into App::new().appdata(...). This allows all your routes to securely access the database without creating a new connection every time.

10. Exercises

  1. 1. Setup an Actix Web project.
  1. 2. Create a struct Product (id, name, price) and derive Serialize.
  1. 3. Create a GET route /products that returns a hardcoded Product as JSON.
  1. 4. Run the server and test it in your browser.

11. MCQs with Answers

Question 1

Which of the following is a highly popular, extremely fast Web Framework for Rust?

Question 2

What crate is the industry standard for converting Rust Structs into JSON?

Question 3

What macro is used to define a route that handles GET requests?

Question 4

What must you derive on a Struct to allow Actix to return it as a JSON response?

Question 5

In Actix, how do you specify the port and IP the server should listen on?

Question 6

If your route is #[get("/users/{id}")], what parameter type do you use to extract the id?

Question 7

What macro is used to define an endpoint that creates data on the server?

Question 8

What must you derive on a Struct to allow Actix to parse an incoming JSON payload into that Struct?

Question 9

If you define a route function but get a 404 Not Found error in the browser, what likely happened?

Question 10

Why are Rust backend frameworks so highly rated for enterprise architecture?

12. Interview Questions

  • Q: Explain how Serde works with Actix Web to handle JSON serialization and deserialization seamlessly.
  • Q: How does Actix handle massive concurrency? (Answer: It utilizes the Tokio async runtime, spawning lightweight tasks to handle incoming requests rather than heavy OS threads).

13. Summary

Building web servers in Rust is no longer a daunting task. Frameworks like Actix Web provide an elegant, macro-driven API that feels similar to Python's Flask or Node's Express. However, beneath the clean syntax lies a high-performance engine capable of serving millions of requests securely, backed by Rust's fearless concurrency and memory safety.

14. Next Chapter Recommendation

Your web server is running, but how do you mathematically prove that it works correctly? In Chapter 27: Testing in Rust, we will explore Rust's built-in testing framework, writing unit tests, integration tests, and ensuring our code never regresses.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·