Use r.Group() to define a common path prefix and middleware for a set of routes, keeping your router configuration DRY and organized. You can nest groups to create deeper hierarchies, and middleware added to a group applies to all routes defined within that scope.
Here is a practical example showing how to group routes by API version and apply authentication middleware only to specific endpoints:
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Simple middleware to simulate authentication
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if c.GetHeader("Authorization") == "" {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Next()
}
}
func main() {
r := gin.Default()
// Group 1: Public routes (no middleware)
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok"})
})
// Group 2: API v1 with a common prefix
v1 := r.Group("/api/v1")
{
// Public endpoint within v1
v1.GET("/users", func(c *gin.Context) {
c.JSON(200, gin.H{"users": []string{"alice", "bob"}})
})
// Group 3: Protected routes within v1
// Middleware applies to all routes inside this block
protected := v1.Group("/admin")
protected.Use(authMiddleware())
{
protected.GET("/dashboard", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Welcome to admin dashboard"})
})
protected.POST("/settings", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "Settings updated"})
})
}
}
r.Run(":8080")
}
In this setup, /api/v1/users is accessible without headers, but /api/v1/admin/dashboard requires the Authorization header because the authMiddleware is attached to the protected group. You can also pass a prefix string directly to Group() if you prefer a one-liner for simple prefixes, like r.Group("/api/v2").GET("/status", handler), but using the block syntax is generally preferred for readability when adding middleware or defining multiple routes.
Remember that middleware order matters: if you define a group, add middleware, and then define another sub-group, the parent middleware executes before the child routes. This pattern scales well as your application grows, allowing you to isolate concerns like versioning, authentication, and logging without cluttering your main router file.