Rust and API: From REST to gRPC — Writing Fast and Safe Code

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

Why Rust is the Ideal Candidate for Working with APIs?

\n\n

If you still think Rust is only about systems programming and replacing C++, you are far behind reality. Rust has confidently burst into the world of web development and backend services. And not just burst in, but carved out a niche where speed, reliability, and memory safety are crucial.

\n\n

But let's be honest: working with APIs in Rust is not just "grab a ready-made library and forget about it." It's an art of balancing the power of the language with development convenience. In this article, we'll cover three main approaches: REST, GraphQL, and gRPC. Each is good in its own way, and the choice depends on your task. Let's go!

\n\n

REST in Rust: A Classic That Works

\n\n

REST remains the most popular API style. It's simple, intuitive, and supported by virtually everything. There are several excellent frameworks for Rust, but the king here is actix-web. It's fast, asynchronous, and has a great ecosystem.

\n\n

Writing a Simple REST Service with actix-web

\n\n

Let's create a mini-service for managing a todo list. We'll need actix-web, serde for serialization, and tokio as the async runtime.

\n\n
// Cargo.toml\n// [dependencies]\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n// serde_json = "1"\n// tokio = { version = "1", features = ["full"] }\n\nuse actix_web::{get, post, web, App, HttpResponse, HttpServer, Responder};\nuse serde::{Deserialize, Serialize};\nuse std::sync::Mutex;\n\n#[derive(Serialize, Deserialize, Clone)]\nstruct Task {\n    id: u32,\n    title: String,\n    completed: bool,\n}\n\nstruct AppState {\n    tasks: Mutex<Vec<Task>>,\n}\n\n#[get("/tasks")]\nasync fn get_tasks(data: web::Data<AppState>) -> impl Responder {\n    let tasks = data.tasks.lock().unwrap();\n    HttpResponse::Ok().json(tasks.clone())\n}\n\n#[post("/tasks")]\nasync fn create_task(data: web::Data<AppState>, body: web::Json<Task>) -> impl Responder {\n    let mut tasks = data.tasks.lock().unwrap();\n    let new_id = tasks.len() as u32 + 1;\n    let task = Task {\n        id: new_id,\n        title: body.title.clone(),\n        completed: false,\n    };\n    tasks.push(task.clone());\n    HttpResponse::Created().json(task)\n}\n\n#[actix_web::main]\nasync fn main() -> std::io::Result<()> {\n    let state = web::Data::new(AppState {\n        tasks: Mutex::new(vec![]),\n    });\n\n    HttpServer::new(move || {\n        App::new()\n            .app_data(state.clone())\n            .service(get_tasks)\n            .service(create_task)\n    })\n    .bind("127.0.0.1:8080")?\n    .run()\n    .await\n}\n
\n\n

The beauty of this code is that it is memory-safe and requires no garbage collector. actix-web handles thousands of requests per second on a single thread using Rust's async mechanisms. Try running it — you'll be amazed at how fast it is even on weak hardware.

\n\n

When is REST Not the Best Choice?

\n\n

REST is great for CRUD operations, but if you need to flexibly query data (e.g., nested resources), REST starts requiring many endpoints. This is where GraphQL comes into play.

\n\n

GraphQL in Rust: Flexibility Without Pain

\n\n

GraphQL allows the client to request exactly the data it needs. For Rust, the best choice is async-graphql. It integrates tightly with actix-web and provides a powerful DSL for describing schemas.

\n\n

Example: API for a Book Library

\n\n

Suppose we want to give clients the ability to query books and authors. With GraphQL, this is done with a single schema.

\n\n
// Cargo.toml\n// [dependencies]\n// async-graphql = "7"\n// async-graphql-actix-web = "7"\n// actix-web = "4"\n// serde = { version = "1", features = ["derive"] }\n\nuse async_graphql::{Context, EmptyMutatio                

Blogs

Book Recommendations