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:
- Basic knowledge of Rust.
- Rust and Cargo installed on your system.
- Familiarity with RESTful APIs.
Setting Up the Project
First, create a new Rust project using Cargo:
cargo new rust_actix_api
cd rust_actix_apiAdd 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 runYour 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:
-
Create a new book:
curl -X POST -H "Content-Type: application/json" -d '{"title": "The Rust Book", "author": "Steve Klabnik"}' http://127.0.0.1:8080/books -
Get all books:
curl http://127.0.0.1:8080/books -
Get a book by ID:
curl http://127.0.0.1:8080/books/{id} -
Update a book
curl -X PUT -H "Content-Type: application/json" -d '{"title": "The Updated Rust Book", "author": "Steve Klabnik"}' http://127.0.0.1:8080/books/{id} -
Delete a book
curl -X DELETE http://127.0.0.1:8080/books/{id}
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!
