Group Routing
Organize routes with prefixes, shared middleware, and hierarchical structure
Route groups allow you to organize related routes under a common prefix and apply shared middleware, making your API structure clean and maintainable.
Basic Route Groups
Create a route group with a common prefix:
package main
import (
"github.com/xraph/forge"
)
func main() {
app := forge.New()
// Create API v1 group
v1 := app.Group("/api/v1")
// All routes in this group will have /api/v1 prefix
v1.GET("/users", getUsersHandler)
v1.POST("/users", createUserHandler)
v1.GET("/users/:id", getUserHandler)
v1.PUT("/users/:id", updateUserHandler)
v1.DELETE("/users/:id", deleteUserHandler)
app.Start(":8080")
}Nested Groups
Groups can be nested to create hierarchical route structures:
func setupRoutes(app *forge.App) {
// Main API group
api := app.Group("/api")
// Version 1 group
v1 := api.Group("/v1")
v1.GET("/health", healthHandler)
// User routes under v1
users := v1.Group("/users")
users.GET("", listUsersHandler) // GET /api/v1/users
users.POST("", createUserHandler) // POST /api/v1/users
users.GET("/:id", getUserHandler) // GET /api/v1/users/:id
users.PUT("/:id", updateUserHandler) // PUT /api/v1/users/:id
users.DELETE("/:id", deleteUserHandler) // DELETE /api/v1/users/:id
// Admin routes under users
admin := users.Group("/admin")
admin.GET("/stats", getUserStatsHandler) // GET /api/v1/users/admin/stats
admin.POST("/bulk", bulkCreateUsersHandler) // POST /api/v1/users/admin/bulk
// Version 2 group with different structure
v2 := api.Group("/v2")
v2.GET("/users", listUsersV2Handler)
v2.GET("/users/:id/profile", getUserProfileHandler)
}Group Middleware
Apply middleware to all routes in a group:
import "github.com/xraph/forge/middleware"
func setupAuthenticatedRoutes(app *forge.App) {
// Create authenticated group with middleware
auth := app.Router().Group("/auth",
forge.WithGroupMiddleware(jwtAuthMiddleware("secret")),
)
auth.GET("/profile", getProfileHandler)
auth.PUT("/profile", updateProfileHandler)
auth.POST("/logout", logoutHandler)
// All routes above require authentication
}
// Authentication middleware
func jwtAuthMiddleware(secretKey string) forge.Middleware {
return func(next forge.Handler) forge.Handler {
return func(ctx forge.Context) error {
token := ctx.Request().Header.Get("Authorization")
if token == "" {
return forge.Unauthorized("authentication required")
}
// Validate token logic here
// Store user info in context
ctx.Set("user_id", extractedUserID)
return next(ctx)
}
}
}import "github.com/xraph/forge/middleware"
func setupSecureAPI(app *forge.App) {
// Apply multiple middleware to group
limiter := middleware.NewRateLimiter(100, 200)
secure := app.Router().Group("/secure",
forge.WithGroupMiddleware(middleware.RequestID()),
forge.WithGroupMiddleware(jwtAuthMiddleware("secret")),
forge.WithGroupMiddleware(middleware.RateLimit(limiter, app.Logger())),
forge.WithGroupMiddleware(middleware.Logging(app.Logger())),
)
secure.GET("/data", getSecureDataHandler)
secure.POST("/upload", uploadSecureFileHandler)
}
// Custom middleware
func customLoggingMiddleware(logger forge.Logger) forge.Middleware {
return func(next forge.Handler) forge.Handler {
return func(ctx forge.Context) error {
start := time.Now()
err := next(ctx)
duration := time.Since(start)
logger.Info("request completed",
"path", ctx.Request().URL.Path,
"duration", duration,
)
return err
}
}
}import "github.com/xraph/forge/middleware"
func setupWithBuiltInMiddleware(app *forge.App) {
// Admin group with built-in middleware
adminLimiter := middleware.NewRateLimiter(10, 20)
admin := app.Router().Group("/admin",
forge.WithGroupMiddleware(middleware.RequestID()),
forge.WithGroupMiddleware(middleware.Recovery(app.Logger())),
forge.WithGroupMiddleware(jwtAuthMiddleware("secret")),
forge.WithGroupMiddleware(requireAdminRole()),
forge.WithGroupMiddleware(middleware.RateLimit(adminLimiter, app.Logger())),
)
admin.GET("/users", adminListUsersHandler)
admin.DELETE("/users/:id", adminDeleteUserHandler)
// Moderator group with different permissions
mod := app.Group("/mod",
forge.WithGroupMiddleware(authMiddleware),
forge.WithGroupMiddleware(requireModeratorRole),
)
mod.GET("/reports", getReportsHandler)
mod.PUT("/reports/:id", updateReportHandler)
}
func requireAdminRole(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user := getUserFromContext(r.Context())
if user.Role != "admin" {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}Group Configuration Options
Configure groups with various options:
func setupConfiguredGroups(app *forge.App) {
// Group with metadata and tags
api := app.Router().Group("/api/v1",
forge.WithGroupTags("api", "v1"),
forge.WithGroupMetadata("version", "1.0.0"),
forge.WithGroupMetadata("deprecated", false),
)
// Internal admin group excluded from schemas
admin := app.Router().Group("/internal/admin",
forge.WithGroupSchemaExclude(), // Excludes from OpenAPI/AsyncAPI/oRPC
forge.WithGroupMiddleware(adminAuthMiddleware()),
)
admin.GET("/debug", debugHandler)
admin.DELETE("/cache", flushCacheHandler)
// These routes won't appear in API documentation
}Schema Exclusion
Exclude entire route groups from API documentation:
// Internal endpoints not for public documentation
internalGroup := app.Router().Group("/internal",
forge.WithGroupSchemaExclude(),
)
internalGroup.GET("/health", internalHealthHandler)
internalGroup.GET("/metrics", metricsHandler)
internalGroup.POST("/reload", reloadConfigHandler)
// Debug group excluded from schemas
debugGroup := app.Router().Group("/debug",
forge.WithGroupSchemaExclude(),
forge.WithGroupMiddleware(debugAuthMiddleware()),
)
debugGroup.GET("/vars", debugVarsHandler)
debugGroup.GET("/pprof", pprofHandler)Use Cases for Schema Exclusion:
- Internal/debug endpoints
- Health checks and monitoring
- Admin-only routes
- Development-only endpoints
- Metrics and profiling routes
Authentication and Authorization Groups
Organize routes by authentication requirements:
func setupAuthGroups(app *forge.App) {
// Public routes (no authentication)
public := app.Group("/public")
public.GET("/health", healthHandler)
public.GET("/docs", docsHandler)
public.POST("/register", registerHandler)
public.POST("/login", loginHandler)
// Authenticated routes
auth := app.Group("/api",
forge.WithGroupMiddleware(authMiddleware),
)
// User routes (authenticated users)
user := auth.Group("/user")
user.GET("/profile", getProfileHandler)
user.PUT("/profile", updateProfileHandler)
user.GET("/orders", getUserOrdersHandler)
// Admin routes (admin users only)
admin := auth.Group("/admin",
forge.WithGroupMiddleware(requireAdminRole),
)
admin.GET("/users", adminListUsersHandler)
admin.GET("/analytics", adminAnalyticsHandler)
admin.POST("/maintenance", maintenanceModeHandler)
// API key routes (service-to-service)
apiKey := app.Group("/service",
forge.WithGroupMiddleware(apiKeyMiddleware),
)
apiKey.POST("/webhook", webhookHandler)
apiKey.GET("/metrics", metricsHandler)
}Versioned API Groups
Manage API versions with groups:
func setupVersionedAPI(app *forge.App) {
// Version 1 - Legacy API
v1 := app.Group("/api/v1",
forge.WithGroupMiddleware(deprecationWarningMiddleware),
forge.WithGroupTags("v1", "deprecated"),
)
v1.GET("/users", getUsersV1Handler)
v1.GET("/posts", getPostsV1Handler)
// Version 2 - Current API
v2 := app.Group("/api/v2",
forge.WithGroupTags("v2", "current"),
)
v2.GET("/users", getUsersV2Handler)
v2.GET("/posts", getPostsV2Handler)
v2.GET("/users/:id/posts", getUserPostsHandler) // New endpoint
// Version 3 - Beta API
v3 := app.Group("/api/v3",
forge.WithGroupMiddleware(betaAccessMiddleware),
forge.WithGroupTags("v3", "beta"),
)
v3.GET("/users", getUsersV3Handler)
v3.GET("/posts", getPostsV3Handler)
v3.GET("/analytics", analyticsHandler) // New feature
}
func deprecationWarningMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-API-Deprecated", "true")
w.Header().Set("X-API-Sunset", "2024-12-31")
next.ServeHTTP(w, r)
})
}Resource-Based Groups
Organize routes around resources:
func setupResourceGroups(app *forge.App) {
api := app.Group("/api/v1")
// User resource
users := api.Group("/users")
users.GET("", listUsersHandler) // GET /api/v1/users
users.POST("", createUserHandler) // POST /api/v1/users
users.GET("/:id", getUserHandler) // GET /api/v1/users/:id
users.PUT("/:id", updateUserHandler) // PUT /api/v1/users/:id
users.DELETE("/:id", deleteUserHandler) // DELETE /api/v1/users/:id
// User sub-resources
users.GET("/:id/posts", getUserPostsHandler) // GET /api/v1/users/:id/posts
users.GET("/:id/followers", getUserFollowersHandler) // GET /api/v1/users/:id/followers
// Post resource
posts := api.Group("/posts")
posts.GET("", listPostsHandler)
posts.POST("", createPostHandler)
posts.GET("/:id", getPostHandler)
posts.PUT("/:id", updatePostHandler)
posts.DELETE("/:id", deletePostHandler)
// Post sub-resources
posts.GET("/:id/comments", getPostCommentsHandler)
posts.POST("/:id/comments", createCommentHandler)
posts.POST("/:id/like", likePostHandler)
posts.DELETE("/:id/like", unlikePostHandler)
}Middleware Inheritance
Groups inherit middleware from parent groups:
func setupMiddlewareInheritance(app *forge.App) {
// Root level middleware
app.Use(corsMiddleware)
app.Use(requestIDMiddleware)
// API group inherits root middleware + adds auth
api := app.Group("/api",
forge.WithGroupMiddleware(authMiddleware),
)
// V1 group inherits API middleware + adds versioning
v1 := api.Group("/v1",
forge.WithGroupMiddleware(versioningMiddleware("v1")),
)
// Admin group inherits V1 middleware + adds admin check
admin := v1.Group("/admin",
forge.WithGroupMiddleware(requireAdminRole),
)
// Final middleware chain for /api/v1/admin/* routes:
// corsMiddleware -> requestIDMiddleware -> authMiddleware ->
// versioningMiddleware -> requireAdminRole -> handler
admin.GET("/users", adminUsersHandler)
}Error Handling in Groups
Handle errors consistently across grouped routes:
func setupErrorHandling(app *forge.App) {
// API group with error handling middleware
api := app.Group("/api",
forge.WithGroupMiddleware(errorHandlingMiddleware),
forge.WithGroupMiddleware(validationMiddleware),
)
api.POST("/users", createUserHandler)
api.PUT("/users/:id", updateUserHandler)
}
func errorHandlingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic in handler: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func validationMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == "POST" || r.Method == "PUT" {
contentType := r.Header.Get("Content-Type")
if !strings.Contains(contentType, "application/json") {
http.Error(w, "Content-Type must be application/json", http.StatusBadRequest)
return
}
}
next.ServeHTTP(w, r)
})
}Best Practices for Route Groups
- Logical Organization: Group routes by functionality, version, or resource
- Middleware Reuse: Apply common middleware at the group level
- Consistent Naming: Use clear, consistent prefixes for groups
- Security Layers: Apply authentication/authorization at appropriate group levels
- Version Management: Use groups to manage API versions effectively
Advanced Group Features
Group Metadata and Tags
func setupAdvancedGroups(app *forge.App) {
// Group with comprehensive metadata
api := app.Group("/api/v1",
forge.WithGroupTags("api", "v1", "production"),
forge.WithGroupMetadata(map[string]any{
"version": "1.0.0",
"maintainer": "api-team@company.com",
"deprecated": false,
"rate_limit": 1000,
"description": "Main API endpoints",
}),
)
api.GET("/users", getUsersHandler)
}Dynamic Group Configuration
func setupDynamicGroups(app *forge.App, config *Config) {
// Configure groups based on environment
var middleware []forge.Middleware
if config.Environment == "development" {
middleware = append(middleware, debugMiddleware)
}
if config.RateLimitEnabled {
middleware = append(middleware, rateLimitMiddleware)
}
api := app.Group("/api", forge.WithGroupMiddleware(middleware...))
api.GET("/health", healthHandler)
}Next Steps
- Learn about Middleware for request processing
- Explore Route Parameters for dynamic routing
- Check out Controllers for organizing route logic
- See Advanced Features for OpenAPI, WebSockets, and more
How is this guide?
Last updated on