
Building Microservices in Rust: A Guide to Using Axum
Axum is a web application framework that focuses on ergonomics and type safety, making it easier to build HTTP services. It leverages the powerful Tokio runtime for asynchronous processing, allowing for high-performance applications. In this guide, we will create a simple microservice that provides a RESTful API to manage a list of tasks.
Setting Up the Project
First, ensure you have Rust installed on your machine. You can install Rust using rustup. Once Rust is set up, create a new project:
cargo new task_manager
cd task_managerNext, add the necessary dependencies in your Cargo.toml file:
[dependencies]
axum = "0.6"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0"This configuration includes Axum for the web framework, Tokio for async support, and Serde for serialization and deserialization of JSON data.
Defining the Data Model
We will define a simple data model for our tasks. Create a new file named models.rs in the src directory:
// src/models.rs
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone)]
pub struct Task {
pub id: usize,
pub title: String,
pub completed: bool,
}This struct represents a task with an ID, a title, and a completion status. The Serialize and Deserialize traits allow us to convert between the Task struct and JSON.
Implementing the Microservice
Now, let's implement the main functionality of our microservice. Open src/main.rs and set up the Axum application:
// src/main.rs
use axum::{
extract::{Json, Path},
response::IntoResponse,
routing::{get, post},
Router,
};
use serde_json::json;
use std::sync::{Arc, Mutex};
use models::Task;
mod models;
type Db = Arc<Mutex<Vec<Task>>>;
#[tokio::main]
async fn main() {
let db: Db = Arc::new(Mutex::new(Vec::new()));
let app = Router::new()
.route("/tasks", post(create_task).get(list_tasks))
.route("/tasks/:id", get(get_task).delete(delete_task))
.layer(axum::AddExtensionLayer::new(db));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn create_task(
Json(task): Json<Task>,
Extension(db): Extension<Db>,
) -> impl IntoResponse {
let mut tasks = db.lock().unwrap();
let id = tasks.len() + 1;
let new_task = Task {
id,
title: task.title,
completed: task.completed,
};
tasks.push(new_task.clone());
(axum::http::StatusCode::CREATED, Json(new_task))
}
async fn list_tasks(Extension(db): Extension<Db>) -> impl IntoResponse {
let tasks = db.lock().unwrap();
Json(tasks.clone())
}
async fn get_task(Path(id): Path<usize>, Extension(db): Extension<Db>) -> impl IntoResponse {
let tasks = db.lock().unwrap();
if let Some(task) = tasks.iter().find(|&t| t.id == id) {
Json(task.clone())
} else {
(axum::http::StatusCode::NOT_FOUND, "Task not found")
}
}
async fn delete_task(Path(id): Path<usize>, Extension(db): Extension<Db>) -> impl IntoResponse {
let mut tasks = db.lock().unwrap();
if let Some(pos) = tasks.iter().position(|t| t.id == id) {
tasks.remove(pos);
(axum::http::StatusCode::NO_CONTENT, "")
} else {
(axum::http::StatusCode::NOT_FOUND, "Task not found")
}
}Explanation of Key Components
- Router: The
Routeris defined to handle different routes. We set up routes for creating, listing, retrieving, and deleting tasks. - State Management: We use
Arc<Mutex<Vec<Task>>>to manage shared state safely across asynchronous requests. TheMutexensures that only one thread can access the task list at a time. - Handlers: Each route has an associated handler function that processes incoming requests. For example,
create_taskhandles POST requests to add a new task.
Running the Microservice
To run your microservice, use the following command:
cargo runYou can interact with the API using tools like curl or Postman. Here are some example requests:
Create a Task
curl -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title": "Learn Rust", "completed": false}'List All Tasks
curl http://localhost:3000/tasksGet a Task by ID
curl http://localhost:3000/tasks/1Delete a Task
curl -X DELETE http://localhost:3000/tasks/1Conclusion
In this tutorial, you've learned how to build a simple microservice in Rust using the Axum framework. By leveraging Rust's safety and performance features, you can create robust and efficient web applications. This microservice can be expanded with additional features such as authentication, database integration, or more complex business logic.
Learn more with useful resources:
