Episode ini mengajarkan cara mengorganisasi route dalam skala besar: subrouter dengan r.Route, group yang berbagi middleware dengan r.Group, dan mount sub-app dengan r.Mount. Kalian juga belajar memisahkan resource router dan menerapkan middleware per subrouter secara selektif.

Aplikasi nyata tidak pernah cukup dengan satu file router. Episode 5 mengajarkan struktur yang membuat route tetap rapi saat aplikasi tumbuh: subrouters, groups, dan mounting. Tiga mekanisme ini adalah cara chi mengelola kompleksitas tanpa memperkenalkan tipe handler baru.
Pola yang akan kalian kuasai di sini menjadi kerangka seluruh struktur project di episode 8. Jika kalian pernah pusing dengan middleware yang tidak jalan di sebagian route, jawabannya hampir selalu ada di episode ini.
r.Route membuat router anak yang mewarisi middleware router induk dan hidup di bawah satu prefix:
func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Route("/api", func(api chi.Router) {
api.Get("/ping", func(w http.ResponseWriter, req *http.Request) {
w.Write([]byte("pong"))
})
api.Get("/health", healthHandler)
})
http.ListenAndServe(":8080", r)
}Semua route di dalam r.Route("/api", ...) otomatis berada di bawah /api. api.Get("/ping", handler) menghasilkan endpoint /api/ping. Router anak mewarisi middleware.Logger dari induk tanpa harus mendaftar ulang.
Resource bisa dipecah lebih dalam dengan route bersarang:
r.Route("/users", func(users chi.Router) {
users.Get("/", listUsers)
users.Post("/", createUser)
users.Route("/{id}", func(user chi.Router) {
user.Get("/", getUser)
user.Put("/", updateUser)
user.Delete("/", deleteUser)
})
})Struktur ini mencerminkan hierarki URL: /users, /users/42, /users/42 dengan method berbeda. user.Get("/", getUser) di dalam subrouter /{id} menangani /users/42.
r.Group membuat scope baru yang memakai middleware tertentu tanpa mengubah path:
r.Group(func(admin chi.Router) {
admin.Use(middleware.BasicAuth("app",
map[string]string{"admin": "rahasia"}))
admin.Get("/admin/stats", statsHandler)
})
r.Get("/public", publicHandler)Group admin memakai Basic Auth, sedangkan route /public yang berada di luar group tidak terpengaruh. r.Group(func(admin chi.Router){:go}... adalah cara memilah area yang butuh proteksi berbeda tanpa prefix terpisah.
Gunakan r.Group ketika:
Gunakan r.Route ketika path-nya memang berbeda dan ingin dibersihkan dari prefix berulang.
r.Mount menempelkan router lain — bahkan milik orang lain — sebagai handler di bawah prefix:
func main() {
r := chi.NewRouter()
v1 := chi.NewRouter()
v1.Use(middleware.Recoverer)
v1.Get("/status", statusHandler)
r.Mount("/v1", v1)
http.ListenAndServe(":8080", r)
}r.Mount("/v1", v1) membuat seluruh route di v1 dapat diakses lewat /v1/status. Router v1 berdiri sendiri dengan middleware Recoverer-nya sendiri — bukan turunan dari r.
Karena Mount menerima http.Handler, kalian bisa menempel apa saja:
fileServer := http.FileServer(http.Dir("./static"))
r.Mount("/assets", http.StripPrefix("/assets", fileServer))http.StripPrefix("/assets", fileServer) menghapus prefix sebelum diteruskan ke file server — pola ini akan kita pakai lagi di episode 7 untuk file statis.
Untuk project nyata, pindahkan setiap resource ke fungsi router sendiri:
func usersRouter() http.Handler {
users := chi.NewRouter()
users.Get("/", listUsers)
users.Post("/", createUser)
users.Route("/{id}", func(user chi.Router) {
user.Get("/", getUser)
user.Put("/", updateUser)
})
return users
}
func main() {
r := chi.NewRouter()
r.Mount("/users", usersRouter())
r.Mount("/products", productsRouter())
http.ListenAndServe(":8080", r)
}usersRouter() dan productsRouter() masing-masing mengembalikan http.Handler, lalu di-mount ke root. Setiap resource kini berdiri sendiri dan mudah diuji.
Kombinasi With dan subrouter memberi kontrol middleware yang presisi:
r.Route("/api", func(api chi.Router) {
api.Use(middleware.Throttle(100))
api.Route("/admin", func(admin chi.Router) {
admin.Use(middleware.BasicAuth("app",
map[string]string{"root": "s3cret"}))
admin.Get("/dashboard", dashboardHandler)
})
api.With(middleware.Compress(5)).Get("/export", exportHandler)
})api.With(middleware.Compress(5)).Get("/export", ...) menerapkan middleware inline hanya untuk satu route — pola yang disebut inline middleware, dan diperkuat lagi di versi 5.3 seperti akan kita bahas di episode 20.
Inti yang harus dibawa pulang:
r.Route membuat subrouter dengan prefix dan mewarisi middleware induk.r.Group membatasi scope middleware tanpa mengubah path.r.Mount menempelkan router atau handler lain sebagai sub-app.http.Handler.With memberi middleware inline per route.Di episode 6 selanjutnya kita fokus pada tulang punggung chi: middleware dan chain — menulis middleware sendiri, memakai r.Use, merangkai dengan middleware.Chain, dan menjelajahi middleware bawaan seperti RequestID, Logger, Recoverer, dan BasicAuth.