Microservices in Rust: Architecture and Practice 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 Microservices?

Microservice architecture has long become the standard for building scalable distributed systems. Usually, Go, Java, or Node.js are chosen for its implementation. However, in recent years, Rust — a systems programming language that combines C++ performance with memory safety — has been attracting more and more attention. In this article on pythonlib.ru, we will analyze how to create microservices in Rust, which libraries to use, and how to organize the architecture.

Rust is ideal for microservices due to:

  • No garbage collector (low latency and predictable memory consumption);
  • Safe memory management at compile time;
  • Excellent async support through the Tokio ecosystem;
  • Small final binary size (ideal for containerization).


Microservice Architecture in Rust

A typical microservice in Rust consists of several layers:

  • HTTP server (usually based on actix-web or axum);
  • Business logic (pure Rust functions);
  • Data layer (working with DB via sqlx or diesel);
  • Async runtime (Tokio);
  • Serialization (most often serde for JSON).

Let's look at the basic architecture using a simple user management service as an example.



1. Project Setup and Dependencies

Create a new Cargo project and add dependencies to Cargo.toml:

[package]name = "user-service"version = "0.1.0"edition = "2021"

[dependencies]actix-web = "4"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio", "postgres"] }dotenv = "0.15"


2. Creating the Main Server Structure

Let's define data models and start the HTTP server:

use actix_web::{web, App, HttpServer, Responder};use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone)]struct User { id: u32, name: String, email: String,}

// In-memory storage for exampleasync fn get_users() -> impl Responder { let users = vec![ User { id: 1, name: "Alice".into(), email: "alice@example.com".into() }, User { id: 2, name: "Bob".into(), email: "bob@example.com".into() }, ]; web::Json(users)}

#[actix_web::main]async fn main() -> std::io::Result<()> { println!("Starting user-service on http://127.0.0.1:8080");

HttpServer::new(|| { App::new() .route("/users", web::get().to(get_users)) }) .bind("127.0.0.1:8080")? .run() .await}

This is a minimal microservice. Note: #[actix_web::main] is a macro that starts the Tokio async runtime.



3. Async Database Operations

A real microservice should persist data. Let's use sqlx for PostgreSQL. Create a .env file with the connection URL:

DATABASE_URL=postgres://user:password@localhost/users_db

Now let's write a function to get users from the database:

use sqlx::PgPool;

async fn get_users_from_db(pool: web::Data<PgPool>) -> impl Responder { // Execute SQL query and map the result to the User struct let users: Vec<User> = sqlx::query_as::<_, User>("SELECT id, name, email FROM users") .fetch_all(pool.get_ref()) .await .unwrap_or_default();

web::Json(users)}

#[actix_web::main]async fn main() -> std::io::Result<()> { dotenv::dotenv().ok(); let database_url = std::env::va

Blogs

Book Recommendations