This episode covers route organization in Fiber v3: grouping routes with RouteGroup, nested groups and prefixes, mounting sub-apps, plus the new RouteChain feature for chaining routes and hostname-based domain routing.

The larger an application grows, the more important route organization becomes. Episode 8 covers groups and domain routing in Fiber v3: grouping routes with prefixes, nested groups, mounting sub-apps, plus the new RouteChain feature and hostname-based domain routing.
These capabilities keep routes structured as the number of endpoints grows. You can separate the admin area from the public one, give each group its own middleware, and even route different hostnames to different routes.
RouteGroup combines routes with a shared prefix and middleware. This is the most common way to divide an application's areas:
api := app.Group("/api", requestID)
v1 := api.Group("/v1", logger.New())
v1.Get("/users", listUsers)
v1.Post("/users", createUser)app.Group("/api", requestID) creates a group with the /api prefix; all routes inside run the requestID middleware and are accessed as /api/users. Groups can be nested: api.Group("/v1") produces the path /api/v1/users and adds logger.New() for the v1 area only.
Groups aren't just about prefixes. Middleware given when creating a group only applies to the routes inside it:
admin := app.Group("/admin", jwtAuth())
admin.Get("/stats", statsHandler)
public := app.Group("/")
public.Get("/products", listProducts)jwtAuth() applies to all /admin/* routes, while the /products route is unaffected. This pattern centralizes admin area authentication without cluttering each handler.
Fiber v3 supports mounting sub-apps — another Fiber application run at a specific path. A sub-app has its own instance:
api := fiber.New()
api.Get("/users", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "hello api"})
})
api.Get("/", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "hello root api"})
})
app.Mount("/api", api)The /api/users and /api/ routes are handled by the api sub-app, while /api2 is handled by the main app. Mounting makes it easy to integrate applications that previously stood alone — for example microservices, admin panels, or dashboards.
Fiber v3 introduces RouteChain, a new way to register routes in a chained style. The call starts with app.Get() to create an instance, then continues with route methods:
app.Get("/", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "hello"})
}, "main", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "hello main"})
})RouteChain lets you register the next route directly from the result of the previous method. The result is more concise code for a set of simple routes — just one call for several routes with their own handlers.
Domain routing in Fiber v3 lets you separate routes by hostname or subdomain. A matching route only runs for that domain:
app.Get("/info", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"app": "default"})
}, "sub.example.com/info", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"app": "sub"})
})A request to /info on sub.example.com triggers the second handler; any other domain gets the first handler. This feature is useful for handling many tenants, subdomain-specific pages, or A/B experiments without adding global routes.
Constraints in Fiber v3 can also be applied to the domain path by defining extra attributes in the RouteAttribute struct:
var customRouteConstraint = fiber.RouteAttribute{At: "custom", Name: "A-Z0-9+"}
app.Get("/test", func(c fiber.Ctx) error {
return c.JSON(fiber.Map{"message": "constraint kustom"})
}, customRouteConstraint)RouteAttribute{At: "custom", Name: "A-Z0-9+"} defines a custom regex constraint used as an exception on the route path. This approach gives full flexibility when built-in rules like :id<int> aren't enough.
curl http://localhost:3000/api/v1/users
curl http://localhost:3000/api/users
curl -H "Host: sub.example.com" http://localhost:3000/infoThe first request triggers the listUsers handler with the requestID and logger.New() middleware. The request to /api/users doesn't match because of the v1 group prefix, and the request with the Host: sub.example.com header triggers the subdomain-specific handler.
Key takeaways:
RouteGroup combines a shared prefix and middleware with the app.Group() method./api/v1/users.app.Mount(path, subApp) runs a Fiber sub-app at a specific path.RouteChain in v3 registers many routes in one chained call.sub.example.com.fiber.RouteAttribute.In the next episode, episode 9, we discuss error handling and logger — custom error handlers, the default ErrorHandler in v3, the NewError function, and integrating the logger middleware with a custom template.