Skip to main content
Route grouping allows you to organize routes under a common path prefix and apply shared middleware, making your application structure cleaner and more maintainable.

Basic Grouping

Create a route group with a shared prefix:
app := fiber.New()

// Create API group
api := app.Group("/api")

// All routes under /api
api.Get("/users", getUsers)       // GET /api/users
api.Post("/users", createUser)    // POST /api/users
api.Get("/posts", getPosts)       // GET /api/posts

Group Middleware

Apply middleware to all routes in a group:
func authMiddleware(c fiber.Ctx) error {
    token := c.Get("Authorization")
    if token == "" {
        return fiber.NewError(fiber.StatusUnauthorized, "Missing token")
    }
    return c.Next()
}

// Group with middleware
api := app.Group("/api", authMiddleware)

// All these routes require authentication
api.Get("/users", getUsers)
api.Post("/users", createUser)
api.Delete("/users/:id", deleteUser)

Multiple Middleware

api := app.Group("/api", logger, authMiddleware, rateLimiter)

// Middleware executes in order:
// 1. logger
// 2. authMiddleware  
// 3. rateLimiter
// 4. route handler

Nested Groups

Create hierarchical route structures:
app := fiber.New()

// Main API group
api := app.Group("/api")

// Version 1 group
v1 := api.Group("/v1")
v1.Get("/users", getUsersV1)      // GET /api/v1/users
v1.Get("/posts", getPostsV1)      // GET /api/v1/posts

// Version 2 group
v2 := api.Group("/v2")
v2.Get("/users", getUsersV2)      // GET /api/v2/users
v2.Get("/posts", getPostsV2)      // GET /api/v2/posts

Deep Nesting

app := fiber.New()

api := app.Group("/api")
v1 := api.Group("/v1")
admin := v1.Group("/admin", adminAuthMiddleware)

// GET /api/v1/admin/users
admin.Get("/users", getAdminUsers)

// POST /api/v1/admin/settings
admin.Post("/settings", updateSettings)

Group Configuration

Named Groups

Assign names to groups for organization:
api := app.Group("/api").Name("api.")

v1 := api.Group("/v1").Name("v1.")
v1.Get("/users", getUsers).Name("users")  // Full name: "api.v1.users"

// Retrieve route by name
route := app.GetRoute("api.v1.users")

Group with Multiple Prefixes

// Apply same handlers to multiple prefixes
app.Group([]string{"/api", "/api-v1"}, middleware)

Organizing by Resource

Structure routes around resources:
app := fiber.New()

api := app.Group("/api")

// User routes
users := api.Group("/users")
users.Get("/", listUsers)           // GET /api/users
users.Post("/", createUser)         // POST /api/users
users.Get("/:id", getUser)          // GET /api/users/:id
users.Put("/:id", updateUser)       // PUT /api/users/:id
users.Delete("/:id", deleteUser)    // DELETE /api/users/:id

// Post routes  
posts := api.Group("/posts")
posts.Get("/", listPosts)           // GET /api/posts
posts.Post("/", createPost)         // POST /api/posts
posts.Get("/:id", getPost)          // GET /api/posts/:id
posts.Put("/:id", updatePost)       // PUT /api/posts/:id
posts.Delete("/:id", deletePost)    // DELETE /api/posts/:id

Middleware Inheritance

Middleware cascades through nested groups:
app := fiber.New()

// Global middleware
app.Use(logger)

// API group middleware
api := app.Group("/api", corsMiddleware)

// Protected group middleware
protected := api.Group("/protected", authMiddleware)

// Request to /api/protected/data goes through:
// 1. logger (global)
// 2. corsMiddleware (api group)
// 3. authMiddleware (protected group)
// 4. handler
protected.Get("/data", getData)

Route Function Pattern

Use the Route function for cleaner organization:
app.Route("/api", func(api fiber.Router) {
    api.Route("/v1", func(v1 fiber.Router) {
        v1.Get("/users", getUsers)
        v1.Post("/users", createUser)
        
        v1.Route("/posts", func(posts fiber.Router) {
            posts.Get("/", listPosts)
            posts.Post("/", createPost)
        })
    })
})

With Names

app.Route("/api", func(api fiber.Router) {
    api.Route("/users", func(users fiber.Router) {
        users.Get("/", getUsers).Name("list")
        users.Post("/", createUser).Name("create")
    }).Name("users.")
}).Name("api.")

// Route name: "api.users.list"

Group Use Cases

API Versioning

app := fiber.New()

v1 := app.Group("/api/v1")
v1.Get("/users", getUsersV1)
v1.Get("/posts", getPostsV1)

v2 := app.Group("/api/v2")
v2.Get("/users", getUsersV2)
v2.Get("/posts", getPostsV2)

Authentication Levels

app := fiber.New()

api := app.Group("/api")

// Public routes - no auth required
public := api.Group("/public")
public.Get("/posts", getPublicPosts)
public.Get("/about", getAbout)

// Authenticated routes - basic auth
auth := api.Group("/auth", basicAuthMiddleware)
auth.Get("/profile", getProfile)
auth.Post("/posts", createPost)

// Admin routes - admin auth required
admin := api.Group("/admin", adminAuthMiddleware)
admin.Get("/users", getAllUsers)
admin.Delete("/posts/:id", deleteAnyPost)

Feature Modules

app := fiber.New()

// E-commerce groups
shop := app.Group("/shop")
products := shop.Group("/products")
cart := shop.Group("/cart", authMiddleware)
checkout := shop.Group("/checkout", authMiddleware)

// Content groups
content := app.Group("/content")
blog := content.Group("/blog")
docs := content.Group("/docs")

Mounting Sub-Applications

Mount a separate Fiber app as a sub-app:
// Create main app
mainApp := fiber.New()

// Create sub-app
adminApp := fiber.New()
adminApp.Get("/dashboard", adminDashboard)
adminApp.Get("/users", adminUsers)

// Mount sub-app at /admin
mainApp.Use("/admin", adminApp)

// Routes:
// GET /admin/dashboard
// GET /admin/users

Sub-App with Middleware

adminApp := fiber.New()
adminApp.Use(adminAuthMiddleware)
adminApp.Get("/dashboard", dashboard)

mainApp.Use("/admin", adminApp)

Best Practices

Put common middleware on groups, specific middleware on routes.
// Authentication for all API routes
api := app.Group("/api", authMiddleware)

// Rate limiting for specific endpoints
api.Post("/upload", rateLimiter, uploadHandler)
Follow a naming convention for route names and groups.
api := app.Group("/api").Name("api.")
users := api.Group("/users").Name("users.")
users.Get("/", handler).Name("list")  // "api.users.list"
Avoid excessive nesting - 2-3 levels is usually enough.
// Good: /api/v1/users
api := app.Group("/api")
v1 := api.Group("/v1")
v1.Get("/users", handler)

// Too deep: /api/v1/public/users/active

Complete Example

package main

import (
    "github.com/gofiber/fiber/v3"
    "github.com/gofiber/fiber/v3/middleware/logger"
)

func main() {
    app := fiber.New()
    
    // Global middleware
    app.Use(logger.New())
    
    // Public routes
    app.Get("/", home)
    app.Get("/about", about)
    
    // API v1
    v1 := app.Group("/api/v1")
    
    // Public API endpoints
    v1.Get("/posts", getPublicPosts)
    
    // Authenticated API endpoints
    auth := v1.Group("/auth", authMiddleware)
    auth.Get("/profile", getProfile)
    auth.Put("/profile", updateProfile)
    
    // User management
    users := auth.Group("/users")
    users.Get("/", listUsers)
    users.Post("/", createUser)
    users.Get("/:id", getUser)
    users.Put("/:id", updateUser)
    users.Delete("/:id", deleteUser)
    
    // Admin routes
    admin := v1.Group("/admin", adminMiddleware)
    admin.Get("/stats", getStats)
    admin.Get("/users", getAllUsers)
    
    app.Listen(":3000")
}

See Also

Routing

Define routes and parameters

Middleware

Apply middleware to routes and groups

Mounting

Mount sub-applications

Context

Work with request context

Build docs developers (and LLMs) love