Skip to Content
Steel is in alpha 🎉
RoutingGroup Routes

Group Routes

Route groups in Steel provide a powerful way to organize related routes and apply middleware to multiple routes at once. Groups support nested hierarchies, middleware inheritance, and clean URL patterns.

Basic Route Groups

Creating Groups

Steel provides two methods for creating route groups:

// Method 1: Create a group and use it group := router.Group() group.GET("/users", getUsersHandler) group.POST("/users", createUserHandler) // Method 2: Create a group with a function (cleaner syntax) router.GroupFunc(func(r router.Router) { r.GET("/users", getUsersHandler) r.POST("/users", createUserHandler) })

Route Patterns with Groups

Use the Route() method to create groups with URL prefixes:

router.Route("/api/v1", func(api router.Router) { api.GET("/users", getUsersHandler) // GET /api/v1/users api.POST("/users", createUserHandler) // POST /api/v1/users api.GET("/users/:id", getUserHandler) // GET /api/v1/users/:id api.PUT("/users/:id", updateUserHandler) // PUT /api/v1/users/:id api.DELETE("/users/:id", deleteUserHandler) // DELETE /api/v1/users/:id })

Nested Groups

Groups can be nested to create hierarchical route structures:

router.Route("/api", func(api router.Router) { // Version 1 API api.Route("/v1", func(v1 router.Router) { v1.GET("/users", v1GetUsersHandler) v1.POST("/users", v1CreateUserHandler) // Admin routes in v1 v1.Route("/admin", func(admin router.Router) { admin.GET("/stats", v1AdminStatsHandler) // GET /api/v1/admin/stats admin.GET("/users", v1AdminUsersHandler) // GET /api/v1/admin/users }) }) // Version 2 API api.Route("/v2", func(v2 router.Router) { v2.GET("/users", v2GetUsersHandler) v2.POST("/users", v2CreateUserHandler) // New features in v2 v2.Route("/analytics", func(analytics router.Router) { analytics.GET("/reports", analyticsReportsHandler) analytics.POST("/events", analyticsEventsHandler) }) }) })

Middleware with Groups

Group-Level Middleware

Apply middleware to all routes within a group:

router.Route("/api", func(api router.Router) { // Middleware applied to all /api/* routes api.Use(corsMiddleware) api.Use(jsonContentTypeMiddleware) api.Use(rateLimitMiddleware) api.GET("/health", healthHandler) api.GET("/status", statusHandler) })

Middleware Inheritance

Child groups inherit middleware from parent groups:

router.Route("/api", func(api router.Router) { // Applied to all API routes api.Use(corsMiddleware) api.Use(rateLimitMiddleware) // Public endpoints api.GET("/health", healthHandler) // Protected endpoints inherit parent middleware api.Route("/protected", func(protected router.Router) { // Additional middleware only for protected routes protected.Use(authMiddleware) protected.Use(rbacMiddleware) protected.GET("/profile", getProfileHandler) protected.POST("/data", createDataHandler) // Admin endpoints inherit all parent middleware protected.Route("/admin", func(admin router.Router) { admin.Use(adminOnlyMiddleware) admin.GET("/users", adminGetUsersHandler) admin.DELETE("/users/:id", adminDeleteUserHandler) }) }) })

Middleware Execution Order

Middleware executes in the order it’s applied through the group hierarchy:

router.Use(globalMiddleware) // 1st: Global middleware router.Route("/api", func(api router.Router) { api.Use(apiMiddleware) // 2nd: API middleware api.Route("/v1", func(v1 router.Router) { v1.Use(versionMiddleware) // 3rd: Version middleware v1.Route("/admin", func(admin router.Router) { admin.Use(adminMiddleware) // 4th: Admin middleware admin.GET("/users", handler) // Handler executes last }) }) }) // Execution order for GET /api/v1/admin/users: // globalMiddleware -> apiMiddleware -> versionMiddleware -> adminMiddleware -> handler

Real-World Examples

RESTful API Structure

func setupAPI(router *steel.SteelRouter) { // Global middleware router.Use(steel.Logger) router.Use(steel.Recoverer) router.Use(corsMiddleware()) // API v1 router.Route("/api/v1", func(api router.Router) { // API-wide middleware api.Use(requestIDMiddleware) api.Use(rateLimitMiddleware(100)) // 100 requests per minute // Public routes api.POST("/auth/login", loginHandler) api.POST("/auth/register", registerHandler) api.GET("/health", healthHandler) // User routes api.Route("/users", func(users router.Router) { users.GET("/", listUsersHandler) // GET /api/v1/users users.GET("/:id", getUserHandler) // GET /api/v1/users/:id // Protected user operations users.Route("/", func(protected router.Router) { protected.Use(authMiddleware) protected.POST("/", createUserHandler) // POST /api/v1/users protected.PUT("/:id", updateUserHandler) // PUT /api/v1/users/:id protected.DELETE("/:id", deleteUserHandler) // DELETE /api/v1/users/:id }) }) // Posts routes api.Route("/posts", func(posts router.Router) { posts.GET("/", listPostsHandler) // GET /api/v1/posts posts.GET("/:id", getPostHandler) // GET /api/v1/posts/:id // Protected post operations posts.Route("/", func(protected router.Router) { protected.Use(authMiddleware) protected.POST("/", createPostHandler) // POST /api/v1/posts protected.PUT("/:id", updatePostHandler) // PUT /api/v1/posts/:id protected.DELETE("/:id", deletePostHandler) // DELETE /api/v1/posts/:id // Comments on posts protected.Route("/:postId/comments", func(comments router.Router) { comments.GET("/", getCommentsHandler) // GET /api/v1/posts/:postId/comments comments.POST("/", createCommentHandler) // POST /api/v1/posts/:postId/comments comments.DELETE("/:id", deleteCommentHandler) // DELETE /api/v1/posts/:postId/comments/:id }) }) }) // Admin routes api.Route("/admin", func(admin router.Router) { admin.Use(authMiddleware) admin.Use(adminMiddleware) admin.Use(auditLogMiddleware) admin.GET("/dashboard", dashboardHandler) admin.GET("/analytics", analyticsHandler) // User management admin.Route("/users", func(userMgmt router.Router) { userMgmt.GET("/", adminListUsersHandler) userMgmt.POST("/:id/ban", banUserHandler) userMgmt.POST("/:id/unban", unbanUserHandler) userMgmt.DELETE("/:id", adminDeleteUserHandler) }) // System management admin.Route("/system", func(system router.Router) { system.GET("/stats", systemStatsHandler) system.POST("/maintenance", maintenanceModeHandler) system.GET("/logs", getLogsHandler) }) }) }) }

Multi-Tenant Application

func setupMultiTenantAPI(router *steel.SteelRouter) { router.Use(steel.Logger) router.Use(steel.Recoverer) // Tenant-specific routes router.Route("/tenant/:tenantId", func(tenant router.Router) { // Tenant resolution middleware tenant.Use(tenantMiddleware) tenant.Use(tenantAuthMiddleware) // API versioning per tenant tenant.Route("/api/v1", func(api router.Router) { api.Use(apiVersionMiddleware("v1")) // Core resources api.Route("/projects", func(projects router.Router) { projects.GET("/", listProjectsHandler) projects.POST("/", createProjectHandler) projects.GET("/:id", getProjectHandler) projects.PUT("/:id", updateProjectHandler) projects.DELETE("/:id", deleteProjectHandler) // Project-specific resources projects.Route("/:projectId", func(project router.Router) { project.Use(projectAccessMiddleware) project.Route("/tasks", func(tasks router.Router) { tasks.GET("/", listTasksHandler) tasks.POST("/", createTaskHandler) tasks.PUT("/:taskId", updateTaskHandler) tasks.DELETE("/:taskId", deleteTaskHandler) }) project.Route("/members", func(members router.Router) { members.GET("/", listMembersHandler) members.POST("/", addMemberHandler) members.DELETE("/:userId", removeMemberHandler) }) }) }) }) // Tenant admin routes tenant.Route("/admin", func(admin router.Router) { admin.Use(tenantAdminMiddleware) admin.GET("/settings", getTenantSettingsHandler) admin.PUT("/settings", updateTenantSettingsHandler) admin.GET("/billing", getBillingHandler) admin.GET("/usage", getUsageStatsHandler) }) }) }

Microservice Gateway Pattern

func setupGateway(router *steel.SteelRouter) { router.Use(steel.Logger) router.Use(steel.Recoverer) router.Use(corsMiddleware()) // Service routing with middleware services := map[string]string{ "users": "http://user-service:8080", "posts": "http://post-service:8080", "comments": "http://comment-service:8080", "media": "http://media-service:8080", } router.Route("/api/v1", func(api router.Router) { // Gateway middleware api.Use(requestIDMiddleware) api.Use(authMiddleware) api.Use(rateLimitMiddleware(1000)) // Route to each service for serviceName, serviceURL := range services { api.Route("/"+serviceName, func(service router.Router) { // Service-specific middleware service.Use(serviceDiscoveryMiddleware(serviceName)) service.Use(circuitBreakerMiddleware(serviceName)) service.Use(retryMiddleware(3)) // Proxy all requests to the service service.Handle("GET", "/*", proxyHandler(serviceURL)) service.Handle("POST", "/*", proxyHandler(serviceURL)) service.Handle("PUT", "/*", proxyHandler(serviceURL)) service.Handle("DELETE", "/*", proxyHandler(serviceURL)) service.Handle("PATCH", "/*", proxyHandler(serviceURL)) }) } // Gateway-specific routes api.Route("/gateway", func(gw router.Router) { gw.GET("/health", gatewayHealthHandler) gw.GET("/metrics", gatewayMetricsHandler) gw.GET("/services", listServicesHandler) }) }) }

Opinionated Groups with OpenAPI

Groups work seamlessly with opinionated handlers and contribute to OpenAPI documentation:

router.Route("/api/v1", func(api router.Router) { api.Route("/users", func(users router.Router) { // OpenAPI documentation is automatically grouped users.OpinionatedGET("/", func(ctx *steel.Context, req ListUsersRequest) (*ListUsersResponse, error) { // Implementation return &ListUsersResponse{}, nil }, steel.WithSummary("List Users"), steel.WithTags("users")) users.OpinionatedPOST("/", func(ctx *steel.Context, req CreateUserRequest) (*CreateUserResponse, error) { // Implementation return &CreateUserResponse{}, nil }, steel.WithSummary("Create User"), steel.WithTags("users")) users.OpinionatedGET("/:id", func(ctx *steel.Context, req GetUserRequest) (*GetUserResponse, error) { // Implementation return &GetUserResponse{}, nil }, steel.WithSummary("Get User"), steel.WithTags("users")) }) })

Mounting External Handlers

Mount existing HTTP handlers or entire applications:

// Mount static file server router.Mount("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./public")))) // Mount another router or framework adminRouter := setupAdminRouter() router.Mount("/admin/", adminRouter) // Mount with middleware router.Route("/legacy", func(legacy router.Router) { legacy.Use(legacyCompatibilityMiddleware) legacy.Mount("/app/", legacyAppHandler) })

Testing Route Groups

Test route groups by creating isolated test routers:

func TestUserRoutes(t *testing.T) { router := steel.NewRouter() // Set up the user routes group router.Route("/api/v1/users", func(users router.Router) { users.Use(testAuthMiddleware) // Use test middleware users.GET("/", listUsersHandler) users.POST("/", createUserHandler) users.GET("/:id", getUserHandler) }) tests := []struct { name string method string path string expectedStatus int }{ {"List users", "GET", "/api/v1/users", 200}, {"Create user", "POST", "/api/v1/users", 201}, {"Get user", "GET", "/api/v1/users/123", 200}, {"Not found", "GET", "/api/v1/users/nonexistent", 404}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(tt.method, tt.path, nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) if w.Code != tt.expectedStatus { t.Errorf("Expected status %d, got %d", tt.expectedStatus, w.Code) } }) } }

Performance Considerations

Performance Tip: Group related routes together and order middleware by frequency of execution and early termination potential.

Efficient Group Organization

// âś… Good: Organize by access patterns router.Route("/api", func(api router.Router) { // Fast, frequently accessed endpoints first api.GET("/health", healthHandler) api.GET("/metrics", metricsHandler) // Authenticated endpoints with heavier middleware api.Route("/auth", func(auth router.Router) { auth.Use(authMiddleware) auth.Use(auditMiddleware) // Group by resource for cache locality auth.Route("/users", userRoutes) auth.Route("/posts", postRoutes) }) })

Route groups in Steel provide a clean, hierarchical way to organize your API while maintaining middleware inheritance and generating comprehensive OpenAPI documentation.

Last updated on