Microservices in Rust: Architecture, Practice, and Code Examples

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 become the standard for modern distributed systems. However, choosing a programming language for implementing microservices is a critical decision. Rust offers a unique combination of C/C++ performance and memory safety, making it an ideal candidate for high-load services.



In this article, we will explore how to build a microservice in Rust using the popular frameworks Actix-web and Tokio. You will learn about architectural principles, asynchronous request processing, and get working code examples.



Microservice Architecture in Rust



Basic Principles



A microservice in Rust is typically built around the following components:

  • HTTP server — handling incoming requests (Actix-web, Warp, Axum).
  • Async runtime — managing concurrency (Tokio, async-std).
  • Business logic — isolated modules with clear boundaries.
  • Data storage — interacting with databases via SQLx or Diesel.
  • Inter-service communication — HTTP/gRPC or message queues (RabbitMQ, Kafka).


Project Structure



Recommended structure for a microservice in Rust:



my_microservice/├── Cargo.toml├── src/│   ├── main.rs          # Entry point, server startup│   ├── routes/          # Route definitions│   ├── handlers/        # Request handlers│   ├── models/          # Data structures (DTOs, entities)│   ├── services/        # Business logic│   ├── repositories/    # Data access layer│   └── config/          # Configuration (env, files)└── tests/               # Integration tests


Practical Example: URL Shortener Service



Let's look at creating a simple microservice for URL shortening. It will accept long URLs and return short identifiers.



Step 1: Project Setup



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



[package]name = "url-shortener"version = "0.1.0"edition = "2021"

[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }serde_json = "1"tokio = { version = "1", features = ["full"] }redis = { version = "0.23", features = ["tokio-comp"] }uuid = { version = "1", features = ["v4"] }dotenv = "0.15"env_logger = "0.10"log = "0.4"


Step 2: Data Models



Let's define the request and response structures:



// models/mod.rsuse serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize)]pub struct CreateUrlRequest { pub url: String,}

#[derive(Debug, Serialize)]pub struct CreateUrlResponse { pub short_code: String, pub original_url: String,}

#[derive(Debug, Serialize)]pub struct ErrorResponse { pub error: String,}


Step 3: Service Layer



Let's implement the business logic using Redis for storage:



// services/url_service.rsuse redis::AsyncCommands;use uuid::Uuid;

pub struct UrlService { redis_client: redis::Client,}

impl UrlService { pub fn new(redis_client: redis::Client) -> Self { Self { redis_client } }

pub async fn create_short_url(&self, original_url: &str) -> Result<String, String> { let short_code = Uuid::new_v4().to_string()[..8].to_string(); let mut conn = self.redis_client .get_multiplexed_async_connection() .await .map_err(|e| format!("Redis connection error: {}", e))?;

// Save to Redis: key — short_code, value — original_url conn.set_ex(&short_code, original_url, 86400) // TTL 24 hours .await .map_err(|e| format!("Redis

Blogs

Book Recommendations