Episode ini membahas pola routing Gin: path parameter dengan :id, wildcard dengan *filepath, query parameter, pencocokan method HTTP, serta RouterGroup untuk prefix v1/v2 dan penanganan 404/405.

Rute adalah jantung REST API: kalian memetakan kombinasi method dan path ke sebuah handler. Gin menawarkan pola routing yang ekspresif dan efisien — path parameter, wildcard, query parameter, hingga pengelompokan rute dengan prefix. Episode 4 membedah semua pola ini secara sistematis, termasuk bagaimana Gin menangani rute yang tidak cocok (404) dan method yang salah (405).
Path parameter memungkinkan nilai dinamis dalam URL. Definisikan dengan prefix titik dua :nama:
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
id := c.Param("id")
c.JSON(200, gin.H{"id": id})
})curl http://localhost:8080/users/42Output {"id":"42"} — nilai 42 ditangkap oleh :id dan diambil dengan c.Param("id"). Perhatikan bahwa nilai selalu bertipe string; konversi ke integer dilakukan manual dengan strconv.
Wildcard (bintang) cocok dengan sisa path, termasuk slash. Pola ini pas untuk melayani file:
r.GET("/files/*filepath", func(c *gin.Context) {
path := c.Param("filepath")
c.String(200, "path: %s", path)
})curl http://localhost:8080/files/css/style.css
curl http://localhost:8080/files/js/app.jsPerbedaan kunci :id vs *filepath:
:id hanya cocok dengan satu segmen path (tanpa slash).*filepath cocok dengan sisa path lengkap (/css/style.css).Data tambahan di URL bisa dibawa sebagai query parameter setelah tanda tanya:
r.GET("/users", func(c *gin.Context) {
page := c.DefaultQuery("page", "1")
limit := c.Query("limit")
c.JSON(200, gin.H{
"page": page,
"limit": limit,
})
})curl "http://localhost:8080/users?page=2&limit=10"c.DefaultQuery("page", "1") mengembalikan nilai dengan default jika tidak ada, sedangkan c.Query("limit") mengembalikan string kosong jika kosong. Gin juga punya c.QueryArray untuk parameter berulang seperti tag=a&tag=b.
Gin memisahkan handler berdasarkan method, sehingga path yang sama bisa melayani banyak operasi:
r.GET("/items", listItems)
r.POST("/items", createItem)
r.PUT("/items/:id", updateItem)
r.DELETE("/items/:id", deleteItem)Method GET bersifat idempoten dan tidak mengubah data, POST membuat resource baru, PUT memperbarui keseluruhan, dan DELETE menghapus. Ini adalah pola CRUD standar REST.
Ada batasan yang harus diingat: dalam satu grup, path dengan method yang sama tidak boleh ambigu. Misalnya:
r.GET("/users/new", h1)
r.GET("/users/:id", h2)Kode di atas akan panic karena Gin tidak bisa memutuskan mana yang cocok untuk /users/new — apakah literal new atau parameter :id. Solusinya: hindari menggabungkan literal dengan parameter di posisi segmen yang sama, atau pakai pola yang tidak ambigu.
Saat API tumbuh, kalian akan ingin mengelompokkan rute dengan prefix yang sama. RouterGroup menjawab kebutuhan ini:
r := gin.Default()
v1 := r.Group("/api/v1")
v2 := r.Group("/api/v2")
v1.GET("/users", func(c *gin.Context) {
c.JSON(200, gin.H{"version": "v1", "users": "..."})
})
v2.GET("/users", func(c *gin.Context) {
c.JSON(200, gin.H{"version": "v2", "users": "..."})
})Fungsi r.Group("/api/v1") mengembalikan *RouterGroup. Rute yang didaftarkan di dalamnya otomatis mendapat prefix /api/v1. Pola ini sangat umum untuk versioning API.
RouterGroup bisa memakai middleware sendiri dan bisa disarangkan (nested):
admin := r.Group("/api/admin")
admin.Use(requireAdmin)
admin.GET("/stats", func(c *gin.Context) {
c.JSON(200, gin.H{"stats": "..."})
})
adminUsers := admin.Group("/users")
adminUsers.GET("", listUsers)
adminUsers.GET("/:id", getUser)Middleware requireAdmin (yang akan kita tulis di episode 6 dan 13) hanya berlaku untuk rute di dalam grup admin. Grouping ini menjaga kebersihan kode saat jumlah rute membengkak.
Gin mengembalikan 404 untuk path yang tidak dikenal dan 405 untuk method yang salah secara default. Kalian bisa menyesuaikan keduanya:
r := gin.Default()
r.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{
"error": "not found",
"message": "route tidak ditemukan",
})
})
r.NoMethod(func(c *gin.Context) {
c.JSON(405, gin.H{"error": "method not allowed"})
})Agar NoMethod aktif, set flag HandleMethodNotAllowed:
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.HandleMethodNotAllowed = true
r.NoMethod(func(c *gin.Context) {
c.JSON(405, gin.H{"error": "method not allowed"})
})Fungsi r.NoRoute(...) dan r.NoMethod(...) memungkinkan response error yang konsisten — penting untuk dokumentasi API dan developer experience.
Inti yang harus dibawa pulang:
:id untuk satu segmen, *filepath untuk sisa path.c.Query dan c.DefaultQuery.NoRoute dan NoMethod.Di episode 5 selanjutnya kita akan membedah request binding & validation — memetakan JSON, query, form, URI, XML, YAML, dan TOML ke struct, memahami tag json, form, uri, binding, serta perbedaan ShouldBind, MustBind, dan Bind.