Skip to main content
A route group lets you collect related routes under a common URL prefix and attach middleware that runs only for those routes. You create a group with app.Group(prefix) and register routes on the returned *Group value exactly as you would on the application.

Creating a group

app := lightning.NewApp()

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

api.Get("/users", listUsers)      // → GET /api/users
api.Post("/users", createUser)    // → POST /api/users
api.Get("/users/:id", getUser)    // → GET /api/users/:id
The *Group type exposes the same HTTP method shortcuts as *Application: Get, Post, Put, Patch, Delete, Head, Options, and AddRoute.

Adding middleware to a group

Call group.Use() to register middleware that runs only for routes in that group. Global middleware registered on the application still runs first.
app := lightning.NewApp()
app.Use(lightning.Logger())   // runs for every request

api := app.Group("/api")
api.Use(func(ctx *lightning.Context) {
    token := ctx.Header("Authorization")
    if token == "" {
        ctx.JSONError(lightning.StatusUnauthorized, "missing token")
        return // do not call ctx.Next() — short-circuit the chain
    }
    ctx.Next()
})

api.Get("/profile", func(ctx *lightning.Context) {
    ctx.JSON(lightning.StatusOK, lightning.Map{"user": "alice"})
})
The execution order for GET /api/profile:
Logger (global)
auth check (group)
  handler
A request to a route outside the group (e.g. GET /ping) skips the group middleware entirely.

Nested groups

Call group.Group(prefix) to create a child group that inherits the parent’s prefix and middleware.
api := app.Group("/api")

v1 := api.Group("/v1")
v1.Get("/users", listUsersV1)   // → GET /api/v1/users

v2 := api.Group("/v2")
v2.Get("/users", listUsersV2)   // → GET /api/v2/users
Middleware is additive: a route in a nested group runs the global middleware, then the parent group’s middleware, then the child group’s middleware, and finally the route handler.

Complete example

The example below builds an /api/v1 group with authentication applied to all routes, and a nested /api/v1/admin group with an additional admin-only check.
package main

import "github.com/go-labx/lightning"

func requireAuth(ctx *lightning.Context) {
    if ctx.Header("Authorization") == "" {
        ctx.JSONError(lightning.StatusUnauthorized, "unauthorized")
        return
    }
    ctx.SetData("userRole", "user")
    ctx.Next()
}

func requireAdmin(ctx *lightning.Context) {
    role, _ := ctx.GetData("userRole").(string)
    if role != "admin" {
        ctx.JSONError(lightning.StatusForbidden, "forbidden")
        return
    }
    ctx.Next()
}

func main() {
    app := lightning.DefaultApp()

    // Public route — no auth required
    app.Get("/ping", func(ctx *lightning.Context) {
        ctx.Text(lightning.StatusOK, "pong")
    })

    // /api/v1 group — all routes require a valid token
    v1 := app.Group("/api").Group("/v1")
    v1.Use(requireAuth)

    v1.Get("/users", func(ctx *lightning.Context) {
        ctx.JSON(lightning.StatusOK, lightning.Map{
            "users": []string{"alice", "bob"},
        })
    })

    v1.Post("/users", func(ctx *lightning.Context) {
        ctx.JSON(lightning.StatusOK, lightning.Map{"status": "created"})
    })

    v1.Get("/users/:id", func(ctx *lightning.Context) {
        id := ctx.Param("id")
        ctx.JSON(lightning.StatusOK, lightning.Map{"id": id})
    })

    // /api/v1/admin nested group — additionally requires admin role
    admin := v1.Group("/admin")
    admin.Use(requireAdmin)

    admin.Get("/stats", func(ctx *lightning.Context) {
        ctx.JSON(lightning.StatusOK, lightning.Map{"requests": 1024})
    })

    app.Run()
}
Request flow for GET /api/v1/users:
Logger     (global, via DefaultApp)
Recovery   (global, via DefaultApp)
requireAuth (v1 group)
  handler
Request flow for GET /api/v1/admin/stats:
Logger      (global)
Recovery    (global)
requireAuth (v1 group)
requireAdmin (admin nested group)
  handler
Group middleware is cached after the first route registration on a group. Call group.Use() before registering any routes to ensure all middleware is included.

Using AddRoute on a group

When you need to register a route for a method determined at runtime, group.AddRoute works the same as app.AddRoute but prepends the group prefix and group middleware.
group.AddRoute("GET", "/status", []lightning.HandlerFunc{
    func(ctx *lightning.Context) {
        ctx.Text(lightning.StatusOK, "ok")
    },
})