0Pricing
Learn Rust Coding · Lesson

REST APIs with Actix-web/Rocket

Develop RESTful APIs using a modern Rust web framework like Actix-web or Rocket, handling routes, requests, and responses.

REST APIs with Actix-web/Rocket is a free Learn Rust Coding lesson on CoddyKit — lesson 1 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Learn Rust Coding learning path, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What are REST APIs?

REST, or REpresentational State Transfer, is an architectural style for networked applications. It defines a set of principles for how web services should communicate.

Think of it as a set of guidelines for building web services that are stateless, cacheable, and use standard HTTP methods to interact with resources.

  • Resources: Anything that can be named, like a user, a product, or an order.
  • URIs: Unique identifiers for resources (e.g., /users/123).
  • HTTP Methods: Standard actions performed on resources (GET, POST, PUT, DELETE).

Why Rust for Web Services?

Rust brings unique advantages to web service development, making it a strong choice for high-performance and reliable APIs:

  • Performance: Rust's zero-cost abstractions mean highly efficient code, often comparable to C/C++.
  • Memory Safety: The ownership system prevents common bugs like null pointer dereferences and data races, leading to more robust services.
  • Concurrency: Rust's async/await model, combined with its safety guarantees, makes building concurrent web services 'fearless'.
  • Reliability: Strong type system and compile-time checks catch many errors early.

Meet Actix-web

For building web services in Rust, we often use frameworks. Actix-web is a powerful, pragmatic, and extremely fast web framework for Rust.

It's built on top of Actix, an actor framework, but you don't need to understand actors to use Actix-web effectively. It's designed for asynchronous operations, making it ideal for I/O-bound tasks like handling many concurrent API requests.

Actix-web provides tools for routing, request/response handling, middleware, and much more, simplifying the creation of complex APIs.

Project Setup & Dependencies

To start, we'll create a new Rust project and add the necessary dependencies. We'll use actix-web for the framework and serde for serializing/deserializing JSON data.

First, create a new project:

cargo new my_rest_api --bin

Then, add these lines to your Cargo.toml file under the [dependencies] section:

[dependencies]
actix-web = "4"
serde = { version = "1.0", features = ["derive"] }

Your First Actix-web Server

Let's write a minimal Actix-web server. This code sets up the basic structure to listen for incoming HTTP requests on port 8080. It doesn't handle any specific routes yet, but it's the foundation.

The #[actix_web::main] macro makes our async fn main compatible with Actix-web's runtime.

use actix_web::{App, HttpServer};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://127.0.0.1:8080");
    HttpServer::new(|| {
        // Our application instance, where we'll add routes
        App::new()
    })
    .bind(("127.0.0.1", 8080))? // Bind to an IP address and port
    .run() // Start the server
    .await // Await its completion
}

Defining a GET Route

Now, let's add a simple route to our server. A route maps an incoming HTTP request path and method (like GET /hello) to a specific handler function.

Our handler function hello_world will simply return a string. web::get().to() registers this handler for GET requests to the /hello path.

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

// A handler function that returns a simple string response
async fn hello_world() -> impl Responder {
    "Hello, Actix-web!"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://127.0.0.1:8080");
    HttpServer::new(|| {
        App::new()
            // Register our route: GET /hello maps to hello_world()
            .route("/hello", web::get().to(hello_world))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Path Parameters for Dynamic Routes

APIs often need to handle dynamic parts in the URL, like an ID or a name. Actix-web uses path parameters to capture these values.

We define a placeholder in the route (e.g., /{name}). In the handler, we use web::Path<String> (or any other type that can be deserialized) to extract the value.

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

// Handler function with a path parameter 'name'
async fn greet_name(name: web::Path<String>) -> impl Responder {
    format!("Hello, {}!", name.into_inner())
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://127.0.0.1:8080");
    HttpServer::new(|| {
        App::new()
            // Route with a dynamic path segment for a name
            .route("/greet/{name}", web::get().to(greet_name))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Handling POST Requests & JSON

For creating or updating resources, we use POST or PUT requests, often sending data in the request body as JSON.

Actix-web makes handling JSON easy with web::Json<T>. We define a Rust struct that matches our expected JSON structure, derive Deserialize from serde, and Actix-web automatically parses the incoming JSON into our struct.

use actix_web::{web, App, HttpServer, Responder};
use serde::{Deserialize, Serialize};

// Define a struct to represent our incoming JSON data
#[derive(Deserialize, Serialize)]
struct User {
    username: String,
    email: String,
}

// Handler for POST requests that accepts a JSON User object
async fn create_user(user: web::Json<User>) -> impl Responder {
    // In a real app, you'd save this user to a database
    format!("User created: {} ({})", user.username, user.email)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://127.0.0.1:8080");
    HttpServer::new(|| {
        App::new()
            // POST /users expects a JSON body and maps to create_user()
            .route("/users", web::post().to(create_user))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

Building a Simple API with State

Let's combine what we've learned to build a small API that manages a list of users in memory. We'll use web::Data to share application-specific state (our user list) across handlers.

std::sync::Mutex is used to safely allow mutable access to our Vec<User> from multiple concurrent requests. We'll have endpoints to GET /users and POST /users.

use actix_web::{web, App, HttpServer, Responder, HttpResponse};
use serde::{Deserialize, Serialize};
use std::sync::Mutex; // For shared mutable state

// Define a User struct that can be serialized/deserialized and cloned
#[derive(Deserialize, Serialize, Clone)]
struct User {
    id: u32,
    username: String,
    email: String,
}

// Application state to hold our users and track next ID
struct AppState {
    users: Mutex<Vec<User>>,
    next_id: Mutex<u32>,
}

// Handler to get all users
async fn get_users(data: web::Data<AppState>) -> impl Responder {
    let users = data.users.lock().unwrap(); // Acquire a lock
    web::Json(users.clone()) // Return users as JSON
}

// Handler to create a new user
async fn create_user(
    data: web::Data<AppState>,
    new_user: web::Json<User>,
) -> impl Responder {
    let mut users = data.users.lock().unwrap();
    let mut next_id = data.next_id.lock().unwrap();

    let user = User {
        id: *next_id,
        username: new_user.username.clone(),
        email: new_user.email.clone(),
    };
    users.push(user.clone());
    *next_id += 1; // Increment for the next user
    HttpResponse::Created().json(user) // Return 201 Created status and user
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    println!("Server running at http://127.0.0.1:8080");
    // Create shared application state
    let app_state = web::Data::new(AppState {
        users: Mutex::new(vec![]), // Initialize with an empty user list
        next_id: Mutex::new(1),    // Start IDs from 1
    });

    HttpServer::new(move || { // 'move' closure to capture app_state
        App::new()
            .app_data(app_state.clone()) // Register shared state with the app
            .route("/users", web::get().to(get_users))
            .route("/users", web::post().to(create_user))
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

API Concepts Quick Check

You've learned how to set up a basic Actix-web server and handle different HTTP requests. Let's test your understanding!

Recap & Next Steps

Well done! You've taken your first steps into building RESTful APIs with Rust and Actix-web.

  • We explored REST API fundamentals and why Rust is a great fit.
  • You learned to set up a basic Actix-web project.
  • We covered defining GET and POST routes.
  • You saw how to extract path parameters and handle JSON payloads.
  • Finally, you built a simple API managing in-memory state using web::Data.

Next, you'll delve deeper into database integration and robust error handling to build even more powerful and production-ready web services!

Frequently asked questions

Is the “REST APIs with Actix-web/Rocket” lesson free?

Yes — the full text of “REST APIs with Actix-web/Rocket” is free to read here on the web, and the Learn Rust Coding course includes 3 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Learn Rust Coding course, upgrade to CoddyKit PRO.

What will I learn in “REST APIs with Actix-web/Rocket”?

Develop RESTful APIs using a modern Rust web framework like Actix-web or Rocket, handling routes, requests, and responses. You practise Learn Rust Coding with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Learn Rust Coding?

No prior experience is required. Learn Rust Coding on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 3, so you can start here or from the beginning and move at your own pace.

How long does the “REST APIs with Actix-web/Rocket” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Learn Rust Coding lesson?

Yes. Every Learn Rust Coding lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. REST APIs with Actix-web/Rocket
  2. Database Integration (SQLx/Diesel)
  3. Authentication and Authorization
← Back to Learn Rust Coding