EvolveDev
Theme toggle is loading

Building a REST API with Rust and Actix-web

Learn how to build a robust REST API with Rust and Actix-web in this step-by-step guide. Discover how to set up a CRUD API for managing books, complete with code examples and instructions.

Published: Aug 13, 2024

Building a REST API with Rust and Actix-web

Introduction

Building a REST API in Rust can be a rewarding experience. In this guide, we'll walk you through the process of creating a simple CRUD (Create, Read, Update, Delete) API using the Actix-web framework.

Prerequisites

To follow this tutorial, you should have:

Setting Up the Project

First, create a new Rust project using Cargo:

cargo new rust_actix_api
cd rust_actix_api

Add the required dependencies in your Cargo.toml:

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

Creating the API

We'll create a simple API for managing a list of books. Each book will have an ID, title, and author.

Define the Book Model
Create a file named models.rs in the src directory and define the Book struct:

// src/models.rs
 
use serde::{Deserialize, Serialize};
use uuid::Uuid;
 
#[derive(Serialize, Deserialize, Clone)]
pub struct Book {
    pub id: Uuid,
    pub title: String,
    pub author: String,
}

Define the State
We'll use an AppState struct to store our books in memory. Create a file named state.rs:

// src/state.rs
 
use crate::models::Book;
use std::sync::Mutex;
 
pub struct AppState {
    pub books: Mutex<Vec<Book>>,
}

Define the Handlers

Create a file named handlers.rs and define the CRUD handlers:

// src/handlers.rs
 
use crate::models::Book;
use crate::state::AppState;
use actix_web::{delete, get, post, put, web, HttpResponse, Responder};
use uuid::Uuid;
 
#[get("/books")]
async fn get_books(data: web::Data<AppState>) -> impl Responder {
    let books = data.books.lock().unwrap();
    HttpResponse::Ok().json(&*books)
}
 
#[get("/books/{id}")]
async fn get_book(data: web::Data<AppState>, book_id: web::Path<Uuid>) -> impl Responder {
    let books = data.books.lock().unwrap();
    let book = books.iter().find(|&book| book.id == *book_id);
 
    match book {
        Some(book) => HttpResponse::Ok().json(book),
        None => HttpResponse::NotFound().body("Book not found"),
    }
}
 
#[post("/books")]
async fn create_book(data: web::Data<AppState>, new_book: web::Json<Book>) -> impl Responder {
    let mut books = data.books.lock().unwrap();
    let mut book = new_book.into_inner();
    book.id = Uuid::new_v4();
    books.push(book.clone());
    HttpResponse::Created().json(book)
}
 
#[put("/books/{id}")]
async fn update_book(
    data: web::Data<AppState>,
    book_id: web::Path<Uuid>,
    updated_book: web::Json<Book>,
) -> impl Responder {
    let mut books = data.books.lock().unwrap();
    let book = books.iter_mut().find(|book| book.id == *book_id);
 
    match book {
        Some(book) => {
            book.title = updated_book.title.clone();
            book.author = updated_book.author.clone();
            HttpResponse::Ok().json(book)
        }
        None => HttpResponse::NotFound().body("Book not found"),
    }
}
 
#[delete("/books/{id}")]
async fn delete_book(data: web::Data<AppState>, book_id: web::Path<Uuid>) -> impl Responder {
    let mut books = data.books.lock().unwrap();
    let index = books.iter().position(|book| book.id == *book_id);
 
    match index {
        Some(index) => {
            books.remove(index);
            HttpResponse::Ok().body("Book deleted")
        }
        None => HttpResponse::NotFound().body("Book not found"),
    }
}

Setting Up the Server

Modify the main.rs file to set up the Actix-web server and configure the routes:

// src/main.rs
 
use actix_web::{web, App, HttpServer};
use std::sync::Mutex;
 
mod handlers;
mod models;
mod state;
 
use handlers::*;
 
#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let app_state = web::Data::new(state::AppState {
        books: Mutex::new(vec![]),
    });
 
    HttpServer::new(move || {
        App::new()
            .app_data(app_state.clone())
            .service(get_books)
            .service(get_book)
            .service(create_book)
            .service(update_book)
            .service(delete_book)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Running the Server

Run the server with the following command:

cargo run

Your API will be running at http://127.0.0.1:8080.

Testing the API
You can use tools like curl or Postman to test your API endpoints. Here are some example commands using curl:

Replace {id} with the actual UUID of a book.

Conclusion

In this tutorial, we built a simple CRUD REST API using Rust and Actix-web. We covered setting up the project, defining models, handlers, and routes, and running the server. Actix-web provides a powerful and efficient framework for building web applications in Rust. Happy coding!

#rust#actix-web#rest-api

Share on:

Copyright © EvolveDev. 2025 All Rights Reserved