If you're starting backend development in Rust, Axum is a great choice.
In this guide, we will build a small API and learn:
- ✅ Path Parameters
- ✅ Query Parameters
- ✅ JSON Request & Response
And most importantly: you’ll get the FULL working code at the end.
What We Are Building
We will create 3 routes:
| Route | Method | Description |
|---|---|---|
/user/:name/:id/:age |
GET | Path parameters |
/search?name=...&age=... |
GET | Query parameters |
/user |
POST | JSON request |
Required Dependencies
We use serde for converting data.
Add this to your Cargo.toml:
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
1. Path Parameters
Example URL
/user/talha/10/22
👉 Here:
name = talhaid = 10age = 22
Code
async fn get_user(Path((name, id, age)): Path<(String, i32, i32)>) -> String {
format!("User Name is: {}\n, age: {}\n and Id is {}", name, age, id)
}
Axum extracts values directly from the URL.
2. Query Parameters
Example URL
/search?name=talha&age=22
Struct
#[derive(Deserialize)]
struct SearchParams {
name: String,
age: i32,
}
Code
async fn search(Query(params): Query<SearchParams>) -> String {
format!("User name is {} and he is now {} years old", params.name, params.age)
}
Axum converts query into a struct automatically.
3. JSON Request & Response (POST)
Request JSON
{
"name": "Talha",
"age": 22
}
Structs
#[derive(Deserialize)]
struct CreateUser {
name: String,
age: i32,
}
#[derive(Serialize)]
struct UserResponse {
message: String,
}
Code
async fn create_user(Json(payload): Json<CreateUser>) -> Json<UserResponse> {
let msg = format!("User {} is {} years old", payload.name, payload.age);
Json(UserResponse {
message: msg,
})
}
Full Working Code (Copy & Run)
This is the complete code you can copy and run directly:
use axum::{
routing::{get, post},
Router,
extract::{Path, Query, Json},
};
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct SearchParams {
name: String,
age: i32,
}
#[derive(Deserialize)]
struct CreateUser {
name: String,
age: i32,
}
#[derive(Serialize)]
struct UserResponse {
message: String,
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/user/:name/:id/:age", get(get_user))
.route("/search", get(search))
.route("/user", post(create_user));
let addr = "127.0.0.1:3000";
println!("Server running on http://{}", addr);
axum::serve(
tokio::net::TcpListener::bind(addr).await.unwrap(),
app,
)
.await
.unwrap();
}
async fn get_user(Path((name, id, age)): Path<(String, i32, i32)>) -> String {
format!("User Name is: {}\n, age: {}\n and Id is {}", name, age, id)
}
async fn search(Query(params): Query<SearchParams>) -> String {
format!(
"User name is {} and he is now {} years old",
params.name, params.age
)
}
async fn create_user(Json(payload): Json<CreateUser>) -> Json<UserResponse> {
let msg = format!("User {} is {} years old", payload.name, payload.age);
Json(UserResponse {
message: msg,
})
}
How to Run
cargo run
Then test in browser or Postman:
Test Path
http://localhost:3000/user/talha/10/22
Test Query
http://localhost:3000/search?name=talha&age=22
Test POST (Important)
Use Postman / curl:
curl -X POST http://localhost:3000/user \
-H "Content-Type: application/json" \
-d '{"name":"Talha","age":22}'
What You Learned
✔ Path parameters → Path<T>
✔ Query parameters → Query<T>
✔ JSON body → Json<T>
✔ Structs + Serde = powerful API
Top comments (0)