Build a Fast, Safe Rust Microservice with Actix-web and SQLite
Learn how to create a simple, high‑performance Rust microservice using Actix‑web, Serde, and SQLite, covering environment setup, project structure, core code for CRUD operations, and testing commands, while highlighting Rust’s memory safety, speed, and low runtime overhead.
In modern software development, microservice architecture has become a mainstream method for building scalable, maintainable applications. Rust, as a systems programming language, offers high performance, memory safety, and strong concurrency, making it an increasingly popular choice for microservice development. This article details how to develop a simple Rust microservice and covers the necessary technical details.
Why Choose Rust for Microservice Development?
Rust provides several advantages:
Memory safety : Rust’s ownership system prevents data races and memory bugs, improving stability and security.
High performance : Without a garbage collector, Rust compiles to native code with performance comparable to C/C++.
No runtime overhead : Rust’s minimal runtime makes it ideal for low‑latency, high‑concurrency microservices.
Strong type system : Rust’s type system catches many errors at compile time, enhancing maintainability.
Creating a Simple Rust Microservice
This article builds a simple web microservice that supports basic CRUD (Create, Read, Update, Delete) operations. It uses actix-web to handle HTTP requests, serde for JSON serialization/deserialization, and SQLite as the database.
Environment Setup
First, ensure the Rust toolchain is installed:
<code>$ rustc --version</code>If not installed, visit the official Rust website for installation instructions.
Next, create a new Rust project:
<code>$ cargo new rust_microservice
$ cd rust_microservice</code>Adding Dependencies
Add the following dependencies to Cargo.toml :
<code>[dependencies]
actix-web = "4.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sqlx = { version = "0.5", features = ["sqlite", "runtime-actix-native-tls"] }
env_logger = "0.9"
tokio = { version = "1", features = ["full"] }
</code>Project Structure
The project consists of two main parts:
Model : defines data structures.
Handler : processes HTTP requests and interacts with the database.
Create the following directory layout:
<code>src/
├── main.rs
├── model.rs
└── handler.rs
</code>Writing main.rs
Configure the Actix‑web application and start the HTTP server in src/main.rs :
<code>use actix_web::{web, App, HttpServer};
use env_logger::Env;
use sqlx::SqlitePool;
mod model;
mod handler;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
env_logger::init_from_env(Env::default().default_filter_or("info"));
// Connect to SQLite database
let pool = SqlitePool::connect("sqlite:database.db").await.expect("Failed to connect to the database");
// Start HTTP server
HttpServer::new(move || {
App::new()
.data(pool.clone())
.route("/items", web::post().to(handler::create_item))
.route("/items", web::get().to(handler::get_items))
.route("/items/{id}", web::get().to(handler::get_item))
.route("/items/{id}", web::put().to(handler::update_item))
.route("/items/{id}", web::delete().to(handler::delete_item))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
</code>Writing model.rs
Define the data structure and database mapping in src/model.rs :
<code>use serde::{Deserialize, Serialize};
use sqlx::FromRow;
#[derive(Serialize, Deserialize, FromRow)]
pub struct Item {
pub id: i32,
pub name: String,
pub description: String,
}
</code>Writing handler.rs
Implement the CRUD handlers in src/handler.rs :
<code>use actix_web::{web, HttpResponse};
use sqlx::{SqlitePool, query_as};
use crate::model::Item;
pub async fn create_item(pool: web::Data<SqlitePool>, item: web::Json<Item>) -> HttpResponse {
let result = sqlx::query("INSERT INTO items (name, description) VALUES (?, ?)")
.bind(&item.name)
.bind(&item.description)
.execute(pool.get_ref())
.await;
match result {
Ok(_) => HttpResponse::Ok().json(item.0),
Err(e) => HttpResponse::InternalServerError().body(format!("Error: {}", e)),
}
}
// Implementations for get_items, get_item, update_item, delete_item follow a similar pattern.
</code>Running and Testing
Create the SQLite database file database.db at the project root and create the table:
<code>CREATE TABLE items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT NOT NULL
);
</code>Run the project:
<code>$ cargo run</code>Test the API with curl commands:
Create an item: curl -X POST http://127.0.0.1:8080/items -H "Content-Type: application/json" -d '{"name":"item1","description":"description1"}'
Get all items: curl http://127.0.0.1:8080/items
Get a single item: curl http://127.0.0.1:8080/items/1
Update an item: curl -X PUT http://127.0.0.1:8080/items/1 -H "Content-Type: application/json" -d '{"name":"updated item","description":"updated description"}'
Delete an item: curl -X DELETE http://127.0.0.1:8080/items/1
Conclusion
By following these steps, we have successfully built a simple Rust microservice application. The article demonstrates key Rust components and techniques for microservice development. Future extensions can include authentication, additional database operations, and logging, leveraging Rust’s performance and safety for robust microservice architectures.
Architecture Development Notes
Focused on architecture design, technology trend analysis, and practical development experience sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.