Когато заглавието ви трябва на две места

Когато заглавието ви трябва на две места

Юни 23, 2026 rust actix-web rest-api web-development performance backend devops programming-languages

Rust + Actix-web: APIs, които летят и нямат проблеми със скалирането

Ще си призная нещо — когато ми трябва бърз REST API, първото нещо, за което посегвам, е Node.js или Python. Знам компромисите: бърза разработка срещу производителност, бърз прототайп срещу надеждност в production. Но какво ако можеш да имаш и двете?

Точно тук идва Rust, а Actix-web е рамката, която прави нещата достъпни за уеб разработчиците.

Защо точно Rust за API-та? (Без захаросване)

Нека започнем с неудобната истина: Rust има стръмна крива на обучение. Не е от най-меките. Borrow checker-ът ще те смири. Ще прекараш часове в дебъгване на lifetimes вместо да пускаш функции.

Но нещо, което най-накрая ми „click"-на: Rust не те кара да избираш между сигурност и скорост. Твоят API ще е memory-safe по подразбиране — без null pointer exceptions, без data races, без garbage collection паузи, които убиват p99 latency-те. Когато обслужваш хиляди заявки в секунда, тази консистентност има значение.

За стартъпи и разработчици, които скалират системи, това означава предвидима производителност без операционния overhead на допълнителен слой за сигурност.

Първи стъпки с Actix-web

Actix-web е вдъхновен от (но не е свързан с) Elixir/Phoenix рамката. Залага на actor модела и предлага ергономичен API, който се усеща познат, ако си работил с Express, Fastify или подобни.

Инициализация на проекта

cargo new rust-api-demo
cd rust-api-demo
cargo add actix-web actix-rt

Първият ти endpoint

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

async fn health_check() -> HttpResponse {
    HttpResponse::Ok().json(serde_json::json!({
        "status": "healthy",
        "version": env!("CARGO_PKG_VERSION")
    }))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .route("/health", web::get().to(health_check))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Този health endpoint? Компилира се за милисекунди и стартира за микросекунди. Пускаш cargo run, удряш localhost:8080/health — и веднага виждаш JSON отговора.

Построяваме истински API: Todos с пълни CRUD операции

Сега да направим нещо по-практично — Todo API с пълен набор от CRUD операции.

Дефинираме модела

use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use std::cell::Cell;

#[derive(Clone, Serialize, Deserialize)]
struct Todo {
    id: u64,
    title: String,
    completed: bool,
}

Целият списък с Todos се съхранява в глобално state чрез Mutex, което прави достъпа thread-safe. Всяка заявка получава клонирана версия на списъка — прост, но ефективен модел за демо.

CRUD handlers-ите

async fn get_todos(data: web::Data<Mutex<Vec<Todo>>>) -> HttpResponse {
    let todos = data.lock().unwrap();
    HttpResponse::Ok().json(&*todos)
}

async fn create_todo(
    data: web::Data<Mutex<Vec<Todo>>>,
    body: web::Json<Todo>,
) -> HttpResponse {
    let mut todos = data.lock().unwrap();
    let new_id = todos.len() as u64 + 1;
    let todo = Todo {
        id: new_id,
        title: body.title.clone(),
        completed: false,
    };
    todos.push(todo);
    HttpResponse::Created().json(todo)
}

async fn update_todo(
    data: web::Data<Mutex<Vec<Todo>>>,
    path: web::Path<u64>,
    body: web::Json<Todo>,
) -> HttpResponse {
    let mut todos = data.lock().unwrap();
    if let Some(todo) = todos.iter_mut().find(|t| t.id == *path) {
        todo.title = body.title.clone();
        todo.completed = body.completed;
        HttpResponse::Ok().json(todo)
    } else {
        HttpResponse::NotFound().body("Todo not found")
    }
}

async fn delete_todo(
    data: web::Data<Mutex<Vec<Todo>>>,
    path: web::Path<u64>,
) -> HttpResponse {
    let mut todos = data.lock().unwrap();
    let len_before = todos.len();
    todos.retain(|t| t.id != *path);
    if todos.len() < len_before {
        HttpResponse::NoContent().finish()
    } else {
        HttpResponse::NotFound().body("Todo not found")
    }
}

Регистрираме routes-ите

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let app_data = web::Data::new(Mutex::new(Vec::<Todo>::new()));
    
    HttpServer::new(move || {
        App::new()
            .app_data(app_data.clone())
            .route("/health", web::get().to(health_check))
            .route("/todos", web::get().to(get_todos))
            .route("/todos", web::post().to(create_todo))
            .route("/todos/{id}", web::put().to(update_todo))
            .route("/todos/{id}", web::delete().to(delete_todo))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Какво прави това специално?

Производителността не е компромис. Единственото нещо, с което се отказваш, е малкото време за настройка в началото. След това — микроскопични latency-та, нулеви GC паузи, memory safety без runtime.

Ако търсиш сериозен backend, който да не те предаде под натоварване, Rust + Actix-web е комбинация, която си струва да изпробваш. Дори и само за да разбереш защо толкова много хора са толкова въодушевени от екосистемата.

Read in other languages:

RU EL CS UZ TR SV FI RO PT PL NB NL HU IT FR ES DE DA ZH-HANS EN