Cum să clonezi title-ul paginii: Ghid practic în JavaScript
Rust + Actix-web: Cum să Construiești API-uri Rapide care chiar Scală
Să fim sinceri — când avem nevoie de un REST API rapid, majoritatea alegem Node.js sau Python. Cunoaștem compromisurile: viteză în dezvoltare versus performanță, prototipare rapidă versus fiabilitate de producție. Dar ce-ar fi dacă ai putea avea ambele?
Asta promite Rust, iar Actix-web este framework-ul care face asta accesibil pentru dezvoltatorii web.
De ce Rust pentru API-uri? (Adevărul Neplăcut)
Înainte să scriem o singură linie de cod, hai să abordăm problema: Rust are o curbă de învățare. Nu e simplă deloc. Borrow checker-ul te va pune la pământ. Vei petrece ore debugging la lifetimes în loc să livrezi features.
Dar ce-am înțeles eu: Rust nu te forțează să alegi între siguranță și viteză. API-ul tău va fi memory-safe by default — fără null pointer exceptions, fără data races, fără garbage collection pauses care îți omoară latențele la p99. Când servești mii de cereri pe secundă, consistența asta contează enorm.
Pentru startup-uri și dezvoltatori care scalază sisteme, înseamnă performanță predictibilă fără overhead operațional de gestionat.
Primii Pași cu Actix-web
Actix-web e inspirat de (dar nu afiliat cu) framework-ul Elixir/Phoenix. Îmbrățișează actor model și oferă un API ergonomic care te face să te simți ca acasă dacă ai folosit Express, Fastify sau alte framework-uri similare.
Setup Proiect
cargo new rust-api-demo
cd rust-api-demo
cargo add actix-web actix-rt
Primul 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
}
Endpoint-ul ăla de health? Compilează în milisecunde și pornește în microsecunde. Rulează cargo run și accesează localhost:8080/health — vei vedea răspunsul JSON instant.
Construim un API Real: Todos cu Operații CRUD
Hai să construim ceva mai practic — un Todo API cu operații CRUD complete.
Definim Modelul de Date
use serde::{Deserialize, Serialize};
use std::sync::Mutex;
use std::cell::Cell;
#[derive(Clone, Serialize, Deserialize)]
struct Todo {
id: u64,
title: String,
completed: bool,
}
#[derive(Default)]
struct AppState {
todos: Mutex<Vec<Todo>>,
next_id: Cell<u64>,
}
Aici definim structura pentru un Todo și starea aplicației. Mutex ne ajută să protejăm accesul la vectorul de todos într-un mediu concurrent, iar Cell<u64> ține minte următorul ID disponibil.
Implementăm CRUD
async fn create_todo(
data: web::Data<AppState>,
body: web::Json<Todo>,
) -> HttpResponse {
let id = data.next_id.get();
data.next_id.set(id + 1);
let todo = Todo {
id,
title: body.title.clone(),
completed: body.completed,
};
data.todos.lock().unwrap().push(todo);
HttpResponse::Created().json(serde_json::json!({
"message": "Todo created",
"id": id
}))
}
async fn get_todos(data: web::Data<AppState>) -> HttpResponse {
let todos = data.todos.lock().unwrap().clone();
HttpResponse::Ok().json(todos)
}
async fn update_todo(
data: web::Data<AppState>,
path: web::Path<u64>,
body: web::Json<Todo>,
) -> HttpResponse {
let id = path.into_inner();
let mut todos = data.todos.lock().unwrap();
if let Some(todo) = todos.iter_mut().find(|t| t.id == id) {
todo.title = body.title.clone();
todo.completed = body.completed;
HttpResponse::Ok().json(todo.clone())
} else {
HttpResponse::NotFound().json(serde_json::json!({
"error": "Todo not found"
}))
}
}
async fn delete_todo(
data: web::Data<AppState>,
path: web::Path<u64>,
) -> HttpResponse {
let id = path.into_inner();
let mut todos = data.todos.lock().unwrap();
let len_before = todos.len();
todos.retain(|t| t.id != id);
if todos.len() < len_before {
HttpResponse::Ok().json(serde_json::json!({
"message": "Todo deleted"
}))
} else {
HttpResponse::NotFound().json(serde_json::json!({
"error": "Todo not found"
}))
}
}
Fiecare funcție handler primește ce are nevoie via dependency injection — web::Data<AppState> pentru starea partajată, web::Json<Todo> pentru body-ul cererii, web::Path<u64> pentru parametrii din URL.
Routing-ul Complet
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let app_state = web::Data::new(AppState::default());
HttpServer::new(move || {
let app_state = app_state.clone();
App::new()
.app_data(app_state)
.route("/todos", web::post().to(create_todo))
.route("/todos", web::get().to(get_todos))
.route("/todos/{id}", web::put().to(update_todo))
.route("/todos/{id}", web::delete().to(delete_todo))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
Pattern-ul e simplu și predictibil: POST pentru creare, GET pentru listare, PUT pentru actualizare, DELETE pentru ștergere.
De Ce Merită?
Când testezi endpoint-urile cu curl sau Postman, vei observa ceva: răspunsurile vin constant, fără jitter, fără garbage collection spikes, fără surprize neplăcute.
Rust și Actix-web nu sunt pentru orice proiect — dacă ai nevoie de un MVP rapid și timeline-uri strânse, Python sau Node.js rămân alegeri valide. Dar când ajungi la momentul în care performanța contează cu adevărat, când fiecare milisecundă impactează experiența utilizatorului, Rust oferă o bază solidă pe care poți construi fără să-ți faci griji despre runtime behavior.
Actix-web face totul și mai accesibil — API-ul e intuitiv, documentația e solidă, iar comunitatea e activă. Dacă vrei să explorezi mai departe, îți recomand să consulți documentația oficială Actix-web pentru a vedea ce mai poți face cu acest framework puternic.
Tag-uri: #rust #actix-web #api #web-development #performance