Membuat project Cargo dari nol, menambahkan dependencies Axum + tokio + tower-http, menulis hello world server pertama, dan memahami struktur folder yang rapi agar project siap tumbuh sampai 25 episode ke depan.

Di episode 2 kita sudah memahami arsitektur: request mengalir dari tokio → hyper → tower → Router → handler. Sekarang saatnya tangan di keyboard. Episode ini adalah fondasi praktis pertama: kalian akan membuat project Cargo, menambahkan dependencies yang tepat, menulis hello world server Axum, dan menata struktur folder agar project tidak menjadi bola kusut saat kita menambahkan CRUD, WebSocket, dan auth di episode-episode berikutnya.
Mengapa struktur folder penting sedini mungkin? Karena Axum menggemakan prinsip library-oriented — komponen hidup terpisah. Project yang menata modul dengan jelas akan jauh lebih mudah di-refactor, diuji, dan dipahami orang lain. Kita akan memilih struktur yang seimbang: tidak over-engineer untuk "hello world", tetapi cukup untuk menampung semua yang akan datang.
Mulai dengan project baru. Nama binary kita pakai axum-api:
cargo new axum-api
cd axum-apiPeriksa isi default yang dibuat cargo:
tree -a.
├── .git
├── .gitignore
├── Cargo.toml
└── src
└── main.rsDependencies inti untuk memulai server:
cargo add axum@0.8
cargo add tokio@1 --features rt-multi-thread,macros,signal
cargo add tower-http --features trace
cargo add serde --features derive
cargo add serde_json
cargo add tracing
cargo add tracing-subscriber --features env-filtercargo add memilih versi terbaru yang kompatibel dan menulis ke Cargo.toml secara otomatis. Cek hasilnya:
[package]
name = "axum-api"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "signal"] }
tower-http = { version = "0.6", features = ["trace"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }Catatan penting soal fitur tokio:
rt-multi-thread — runtime tokio multi-thread (bukan current-thread).macros — untuk #[tokio::main].signal — untuk menangkap sinyal shutdown di episode 16.Tanpa fitur yang tepat, kode tidak akan compile meskipun crate terpasang — fitur tokio bersifat opt-in secara sengaja untuk menjaga ukuran binary.
Tulis server pertama di src/main.rs:
use axum::{routing::get, Router};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, Axum!" }));
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
println!("listening on {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}Mari bedah baris per baris:
#[tokio::main] — macro yang membungkus main untuk menjalankan async runtime tokio multi-thread.Router::new() — router kosong; setiap route ditambahkan via .route(...).get(|| async { "Hello, Axum!" }) — handler GET yang berupa closure. String literal &'static str otomatis IntoResponse, jadi langsung menjadi body respons.axum::serve(listener, app) — fungsi utama Axum 0.8 untuk menjalankan server; menerima TcpListener (tokio) dan router. Versi lama memakai tokio::net::TcpListener dengan axum::Server dari hyper — sejak 0.7, axum::serve adalah cara modern.Jalankan server:
cargo runUji di terminal lain:
curl http://127.0.0.1:3000/Hello, Axum!Tip
Gunakan cargo watch -x run agar server otomatis restart setiap file berubah — menghemat waktu saat eksperimen. Install dengan cargo install cargo-watch.
Response "listening on ..." sebaiknya diganti dengan logging terstruktur — kita akan memakai tracing secara penuh di episode 17, tapi memulai lebih awal tidak ada salahnya:
use axum::{routing::get, Router};
use tokio::net::TcpListener;
use tracing::info;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt()
.with_env_filter("axum_api=debug,tower_http=debug")
.init();
let app = Router::new().route("/", get(|| async { "Hello, Axum!" }));
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
info!("server berjalan di {}", listener.local_addr().unwrap());
axum::serve(listener, app).await.unwrap();
}with_env_filter memungkinkan kita mengontrol level log lewat env var RUST_LOG saat runtime — pola yang akan kita formalisasi di episode 16 dan 17.
Untuk episode ini, satu file main.rs sudah cukup. Tapi mari kita siapkan struktur yang akan dipakai sepanjang series — dipecah menjadi modul per domain:
src
├── main.rs
├── routes
│ ├── mod.rs
│ ├── health.rs
│ └── hello.rs
├── handlers
│ ├── mod.rs
│ └── hello.rs
├── models
│ └── mod.rs
├── state.rs
└── error.rsPenjelasan per folder:
| Folder | Isi | Dibahas Di |
|---|---|---|
routes | Definisi Router per domain | Episode 4 |
handlers | Implementasi handler | Episode 4-6 |
models | Struktur data & skema serde | Episode 5, 9 |
state.rs | App state (DB pool, config) | Episode 7 |
error.rs | Tipe error & IntoResponse | Episode 6 |
Tapi jangan menyalin struktur ini ke project hello world kalian sekarang. Prinsip yang sehat: pecah modul saat file sudah terasa padat atau saat kalian butuh menambah domain baru. Untuk episode 3-8, satu main.rs tetap paling praktis — kita baru benar-benar membagi modul saat project masuk fase CRUD di episode 9.
Note
Aturan praktis arsitektur Rust: jangan buat folder abstrak kosong. Mulailah dari satu file, dan pecah ketika ada dua modul yang benar-benar saling bebas (misalnya routes vs models). Struktur di atas adalah tujuan akhir, bukan titik awal.
Sebelum lanjut, pastikan tiga hal beres:
cargo check # cepat, pastikan tidak ada error tipe
cargo run # pastikan server bisa start
curl http://127.0.0.1:3000/ # pastikan respons benarcargo check jauh lebih cepat daripada cargo build dan sebaiknya jadi refleks setiap selesai mengubah kode — kita akan memakainya terus sepanjang series.
Pada episode 3 ini kalian sudah menulis server Axum pertama:
cargo new, dependencies ditambah via cargo add.rt-multi-thread, macros, signal.axum::serve di atas TcpListener tokio.tracing mulai terpasang sejak dini.Di episode 4 selanjutnya kita akan memperluas routing: nested routes, path params, query params, wildcard, fallback, dan merge — dasar yang wajib dikuasai sebelum membangun REST API sungguhan. Pastikan server hello world kalian berjalan, karena episode 4 akan mengubahnya menjadi router multi-route!