If you're coming from Python and have used FastAPI, learning Rust’s Axum can feel confusing at first. But the good news is: the core ideas are almost the same.
Let’s break down this simple Axum server step by step and compare it with FastAPI so it clicks instantly.
🧩 The Full Code
use axum::{ routing::get, Router, };
use std::net::SocketAddr;
#[tokio::main]
async fn main() {
// 1. Create a route
let app = Router::new()
.route("/", get(root));
// 2. Define address
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("Server running at http://{}", addr);
// 3. Run server
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
// 4. Handler function
async fn root() -> &'static str {
"Hello, Axum! Hello guys how are you!!!"
}
🧠 Big Picture
This program does 4 simple things:
- Create a web app
- Define a route (
/) - Start a server on
127.0.0.1:3000 - Return a response when someone visits
🔹 Step 1: Importing Things
use axum::{ routing::get, Router };
use std::net::SocketAddr;
What this means:
- Bring
Router→ to create the app - Bring
get→ to define GET routes - Bring
SocketAddr→ to define IP + port
🐍 FastAPI equivalent:
from fastapi import FastAPI
🔹 Step 2: Async Main Function
#[tokio::main]
async fn main() {
What this means:
Rust normally doesn’t support async in main, so we use Tokio (an async runtime).
👉 This is like saying:
“Run this program using async engine”
🐍 FastAPI equivalent:
import asyncio
async def main():
...
asyncio.run(main())
🔹 Step 3: Create the App
let app = Router::new()
.route("/", get(root));
What this means:
- Create a new app (
Router::new()) - Add a route
/ - When someone sends a GET request, call
root
🐍 FastAPI equivalent:
app = FastAPI()
@app.get("/")
async def root():
return "Hello"
👉 Same idea, different syntax.
🔹 Step 4: Define Address
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
What this means:
Your server will run on:
http://127.0.0.1:3000
🐍 FastAPI equivalent:
uvicorn.run(app, host="127.0.0.1", port=3000)
🔹 Step 5: Bind the Server
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
What this means:
- Open port
3000 - Start listening for incoming requests
Think of it like:
“Open the door and wait for visitors”
🐍 Python equivalent:
server.bind(("127.0.0.1", 3000))
server.listen()
🔹 Step 6: Start the Server
axum::serve(listener, app).await.unwrap();
What this means:
- Take incoming requests from
listener - Pass them to your
app(Router) - Run forever
🐍 FastAPI equivalent:
uvicorn.run(app)
🔹 Step 7: Handler Function
async fn root() -> &'static str {
"Hello, Axum! Hello guys how are you!!!"
}
What this means:
When someone visits /, this function runs and returns a response.
🐍 FastAPI equivalent:
@app.get("/")
async def root():
return "Hello, Axum!"
🔁 Full Request Flow
Here’s what happens when you open your browser:
Browser → http://127.0.0.1:3000/
↓
TcpListener (listening)
↓
Axum Server
↓
Router matches "/"
↓
root() function runs
↓
Response sent back
Now Full fastapi code:
from fastapi import FastAPI
import uvicorn
# 1. Create app
app = FastAPI()
# 2. Create route
@app.get("/")
async def root():
return "Hello, Axum! Hello guys how are you!!!"
# 3. Run server
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=3000)
🔹 Key Difference (important)
👉 In Axum (Rust):
You manually:
bind address
create listener
start server
👉 In FastAPI (Python):
uvicorn.run() does everything for you
Simple Mental Model
| Concept | Axum | FastAPI |
|---|---|---|
| App | Router::new() |
FastAPI() |
| Route | .route("/", get(...)) |
@app.get("/") |
| Handler | async fn root() |
async def root() |
| Server start | axum::serve(...) |
uvicorn.run() |
| Address | SocketAddr |
host + port |
Final Summary
This whole Axum program means:
“Create a web server that listens on port 3000, and when someone visits
/, return a simple message.”
If you're already comfortable with FastAPI, you're much closer to mastering Axum than you think. The concepts are the same — Rust just makes things more explicit and safe.
Top comments (0)