Episode ini membangun project chi pertama yang berjalan: membuat router dengan chi.NewRouter, mendaftarkan handler pertama, dan menjalankan server dengan http.ListenAndServe. Kalian juga belajar struktur Handler dan ServeHTTP, serta mengembalikan respons JSON secara manual.

Cukup teori. Episode 3 mengajak kalian membangun project chi pertama yang benar-benar berjalan: hello world. Ini momen paling penting dalam sebuah series — ketika kalian melihat route pertama hidup dan merespons request lewat curl, semua konsep di episode 1 dan 2 mulai terasa nyata.
Target episode ini: membuat server kecil dengan dua endpoint, memahami apa itu http.Handler dan ServeHTTP, serta mengembalikan JSON secara manual tanpa bantuan library tambahan. Semua pola ini akan menjadi fondasi seluruh episode berikutnya.
Pastikan environment episode 0 sudah siap. Buat module dan install chi v5:
mkdir hello-chi
cd hello-chi
go mod init github.com/username/hello-chi
go get github.com/go-chi/chi/v5@latestgo get github.com/go-chi/chi/v5@latest menambahkan dependency ke go.mod. Setelah itu, buat file main.go — seluruh aplikasi hello world kita hidup di satu file ini.
package main
import (
"net/http"
"github.com/go-chi/chi/v5"
)
func main() {
r := chi.NewRouter()
r.Get("/", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Halo Dunia"))
})
http.ListenAndServe(":8080", r)
}chi.NewRouter() membuat router kosong, lalu r.Get("/", handler) mendaftarkan handler untuk method GET pada path root. Handler ditulis sebagai closure yang menerima http.ResponseWriter dan *http.Request — bentuk yang sama persis dengan http.HandlerFunc.
Jalankan server, lalu test dengan curl dari terminal lain:
go run main.gocurl -i http://localhost:8080/Respons yang diharapkan: status 200 OK, header dari http.Server, dan body Halo Dunia. curl -i http://localhost:8080/ menampilkan header sekaligus body sehingga kalian bisa melihat keseluruhan respons.
Tip
Karena r adalah http.Handler, kalian juga bisa menjalankannya dengan http.ListenAndServe di goroutine dan menangani shutdown — kita akan membahasnya di episode 10.
Sebenarnya sebuah handler cukup mengimplementasikan satu method:
type Handler interface {
ServeHTTP(w http.ResponseWriter, req *http.Request)
}Saat kita menulis func(w http.ResponseWriter, req *http.Request), Go otomatis mengubahnya menjadi http.HandlerFunc yang mengimplementasikan interface tersebut. Di sinilah letak keindahan chi: tidak ada tipe handler khusus. Handler chi adalah handler net/http.
Untuk project nyata, handler sebaiknya bukan closure anonim di dalam main:
func helloHandler(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("Halo Dunia"))
}
func main() {
r := chi.NewRouter()
r.Get("/", helloHandler)
http.ListenAndServe(":8080", r)
}Pola r.Get("/", helloHandler) memakai fungsi bertipe http.HandlerFunc, yang otomatis dipakai chi tanpa konversi manual.
REST API hampir selalu mengembalikan JSON. Manual encoding cukup sederhana:
func healthHandler(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]string{
"status": "ok",
})
}Urutannya penting: set header dulu, tulis status, baru encode body. json.NewEncoder(w).Encode(...) menulis JSON langsung ke ResponseWriter dengan escaping yang benar.
Tambahkan endpoint kesehatan ke router:
func main() {
r := chi.NewRouter()
r.Get("/", helloHandler)
r.Get("/health", healthHandler)
http.ListenAndServe(":8080", r)
}Sekarang test kedua endpoint:
curl -i http://localhost:8080/healthcurl -i http://localhost:8080/health harus mengembalikan status 200 OK dengan header Content-Type: application/json dan body {"status":"ok"}.
Setiap handler di chi bisa dipindahkan ke http.ServeMux atau http.ListenAndServe tanpa perubahan. Demikian juga sebaliknya: handler net/http apa pun bisa didaftarkan ke chi. Prinsip inilah yang membuat chi kompatibel dengan seluruh ekosistem.
Untuk membuktikannya, ganti router dengan http.ServeMux:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", helloHandler)
http.ListenAndServe(":8080", mux)
}Handler helloHandler berjalan mulus tanpa menyentuh satu baris pun. http.NewServeMux() inilah yang dibungkus chi dengan tambahan params, subrouter, dan middleware — topik kita mulai episode 4.
Inti yang harus dibawa pulang:
go mod init lalu go get github.com/go-chi/chi/v5@latest.chi.NewRouter() lalu r.Get("/", handler) adalah resep dasar.http.Handler biasa; tidak ada tipe khusus.http.ListenAndServe(":8080", r).net/http — handler bisa bolak-balik tanpa modifikasi.Di episode 4 selanjutnya kita masuk ke jantung router: routing, params, dan patterns — method routing, path param {id}, wildcard {path:*}, regex {id:[0-9]+}, sampai NotFound dan MethodNotAllowed. Ini akan mengubah cara kalian memandang URL.