How to Write a REST API in Rust in 30 Minutes: A Step-by-Step Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Rust for REST API?



Rust is a modern systems programming language that combines the performance of C/C++ with memory safety. In recent years, it has been actively used for building high-load web services and REST APIs. Thanks to the Actix-web framework, you can write a full-fledged REST API in Rust in just 30 minutes, even if you're just starting to learn the language.



In this article, we will create a simple but functional REST API for managing a Todo List. You will learn:



  • How to set up a Rust project with Actix-web
  • How to implement CRUD operations (Create, Read, Update, Delete)
  • How to work with JSON requests and responses
  • How to use routing and handlers


Ready? Let's go!



Step 1: Project Setup and Dependency Installation



First, make sure you have Rust installed. If not, run the command:



curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh


Create a new project:



cargo new rust_todo_apicd rust_todo_api


Open Cargo.toml and add the following dependencies:



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


Here:

  • actix-web — a powerful asynchronous web framework
  • serde — for JSON serialization/deserialization
  • tokio — an asynchronous runtime (Actix-web uses it under the hood)


Run cargo build to download and compile the dependencies. This will take 2-3 minutes.



Step 2: Creating the Data Model and Storage



In this project, we won't connect a database — for simplicity, we'll use an in-memory vector. Let's create a structure for the task.



Open src/main.rs and write the following code:



use actix_web::{web, App, HttpServer, HttpResponse, middleware};use serde::{Deserialize, Serialize};use std::sync::Mutex;

// Task model#[derive(Serialize, Deserialize, Clone)]struct Task { id: u32, title: String, completed: bool,}

// Task storage (thread-safe)struct AppState { tasks: Mutex<Vec<Task>>,}


Explanation:

  • #[derive(Serialize, Deserialize)] — automatically generates methods for working with JSON
  • Mutex — ensures safe access to data from multiple threads
  • AppState — will be stored in the application state


Step 3: Implementing CRUD Handlers



Now let's add handler functions for each HTTP method. Insert the following code after the struct declarations:



// Get all tasksasync fn get_tasks(data: web::Data<AppState>) -> HttpResponse {    let tasks = data.tasks.lock().unwrap();    HttpResponse::Ok().json(tasks.clone())}

// Create a new taskasync fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> HttpResponse { let mut tasks = data.tasks.lock().unwrap(); let new_id = tasks.len() as u32 + 1; let task = Task { id: new_id, title: body.title.clone(), completed: body.completed, }; tasks.push(task.clone()); HttpResponse::Created().json(task)}

// Update a taskasync fn update_task( data: web::Data<AppState>, path: web::Path<u32>, body: web::Json<Task>,) -> HttpResponse { let mut tasks = data.tasks.lock().unwrap(); let id = path.into_inner(); if let Some(task) = tasks.iter_mut().find(|t| t.id == id) { task.title = body.title.clone(); task.completed = body.completed;

Blogs

Book Recommendations