Part 6: Building Your First Rust Web Server (Hands-On)
What You’ll Learn in This Part
In this part of the Rust Web Development tutorial series, you will:
- Understand how a Rust web server works
- Choose a modern Rust web framework
- Create your first HTTP server
- Define routes and handlers
- Run and test a local web server
By the end of this article, you’ll have a working Rust web server running on your local machine.
Prerequisites
Before starting this part, make sure:
- Rust and Cargo are installed
- You understand basic Rust syntax
- You’ve completed Part 2 and Part 3 of this series
Choosing a Rust Web Framework
Rust has multiple web frameworks, but for modern web development we need:
- Async-first design
- Clean routing
- Strong ecosystem support
For this tutorial, we will use Axum, a modern and widely adopted Rust web framework built on Tokio.
Why Axum?
- Fully async
- Type-safe routing
- Excellent performance
- Simple and clean API
Creating a New Rust Web Project
Create a new project using Cargo:
cargo new rust_web_server
cd rust_web_serverProject structure:
rust_web_server/
├── Cargo.toml
└── src/
└── main.rsAdding Web Dependencies
Open Cargo.toml and update it:
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }Explanation:
axum→ Web frameworktokio→ Async runtime required for web servers
Writing Your First Web Server
Open src/main.rs and replace the content:
use axum::{routing::get, Router};
async fn home() -> &'static str {
"Hello from Rust Web Server!"
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(home));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
Understanding the Code
1. Route Handler
async fn home() -> &'static str {
"Hello from Rust Web Server!"
}async fn→ async function- Returns a static string as HTTP response
2. Router Configuration
let app = Router::new()
.route("/", get(home));- Defines a GET route
/ - Maps it to the
homehandler
3. Server Startup
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();- Binds server to localhost port 3000
- Starts HTTP server
Running the Web Server
Start the server:
cargo runOutput:
Listening on http://127.0.0.1:3000Open your browser and visit:
http://localhost:3000You should see:
Hello from Rust Web Server!🎉 Congratulations! Your first Rust web server is running.
Adding Multiple Routes
Let’s add more routes.
Update main.rs:
async fn about() -> &'static str {
"About page"
}
let app = Router::new()
.route("/", get(home))
.route("/about", get(about));Now visit:
/→ Home/about→ About page
Returning JSON Responses
Add dependency in Cargo.toml:
serde = { version = "1", features = ["derive"] }
Update code:
use axum::Json;
use serde::Serialize;
#[derive(Serialize)]
struct ApiResponse {
message: String,
}
async fn api() -> Json<ApiResponse> {
Json(ApiResponse {
message: "Rust API response".to_string(),
})
}Add route:
.route("/api", get(api))Now /api returns JSON.
Testing with curl
curl http://localhost:3000/apiResponse:
{"message":"Rust API response"}
Common Beginner Mistakes
- Forgetting
#[tokio::main] - Missing async keyword
- Incorrect dependency versions
- Port already in use
