How to Write a REST API in Rust in 30 Minutes: For Beginners

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++ with memory safety. In recent years, it has become a popular choice for building high-load web services. If you are already familiar with Python and want to try something new, Rust will be an excellent step forward.

In this article, we will create a full-fledged REST API for managing a task list (Todo List) in Rust using the Actix-web framework. The entire process will take no more than 30 minutes if you follow the instructions.



1. Environment Setup

Installing Rust

If you don't have Rust installed yet, run the following command in the terminal (Linux/macOS):

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

For Windows, download the installer from the official website.

After installation, check the version:

rustc --version


Creating a Project

Let's create a new project using Cargo, the Rust package manager:

cargo new rust_todo_apicd rust_todo_api


Adding Dependencies

Open the Cargo.toml file and add the following dependencies:

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

Save the file. Cargo will automatically download all libraries on the first build.



2. Creating the Data Model and Storage

Before writing endpoints, let's define the data structure. We will use a simple in-memory vector (for demonstration purposes).



Task Model

Create the file src/models.rs:

use serde::{Deserialize, Serialize};

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


Global Storage

In src/main.rs, we will add global state. For thread safety, we use Mutex:

use std::sync::Mutex;use actix_web::{web, App, HttpServer, HttpResponse, HttpRequest, Responder};use models::Task;

mod models;

struct AppState { tasks: Mutex<Vec<Task>>, next_id: Mutex<u32>,}


3. Implementing CRUD Endpoints

Now let's write the main handlers. They will all receive the application state via web::Data.



GET /tasks — Get All Tasks

async fn get_tasks(data: web::Data<AppState>) -> impl Responder {    let tasks = data.tasks.lock().unwrap();    HttpResponse::Ok().json(tasks.clone())}


POST /tasks — Create a New Task

async fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder {    let mut tasks = data.tasks.lock().unwrap();    let mut next_id = data.next_id.lock().unwrap();    let new_task = Task {        id: *next_id,        title: body.title.clone(),        completed: false,    };    tasks.push(new_task.clone());    *next_id += 1;    HttpResponse::Created().json(new_task)}


GET /tasks/{id} — Get Task by ID

async fn get_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder {    let id = path.into_inner();    let tasks = data.tasks.lock().unwrap();    match tasks.iter().find(|t| t.id == id) {        Some(task) => HttpResponse::Ok().json(task),        None => HttpResponse::NotFound().json("Task not found"),    }}


DELETE /tasks/{id} — Delete a Task

async fn delete_task(data: web::Data<AppState>, path: web::Path<u32>) -> impl Responder {    let id = path.into_inner();    l                

Blogs

Book Recommendations