Automatic Router Generation
Overview
The goframe HTTP generator provides automatic router generation by scanning your handler methods for special documentation comments and generating the corresponding route registration code. This eliminates the need to manually maintain route definitions.
How It Works
The system works in three main steps:
- Package Discovery: Automatically discovers root handler packages (containing
router.go
andregistry.go
) and their subfolders - Annotation Scanning: Scans Go files for methods with
goframe:http_route
comments across all handler packages - Code Generation: Generates router registration code using
http.ServeMux.HandleFunc
Handler Structure
Define your handlers using the standard http.HandlerFunc
pattern:
type UserHandler struct {
userService *UserService
}
func (h *UserHandler) CreateUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Your business logic here
return httpx.JSON.Created(nil), nil
})
}
Route Annotation Syntax
Use the goframe:http_route
comment above your handler methods to define routes:
Basic Route Definition
// goframe:http_route path=/users method=GET
func (h *UserHandler) GetUsers() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Implementation
})
}
Syntax
// goframe:http_route path=/path method=METHOD
// goframe:http_route path=/path method=[GET,POST]
Parameters:
path
- The URL path for the route (required)method
- HTTP method(s). Single:GET
or multiple:[GET,POST]
(default:GET
)
Examples
Simple GET Route
// goframe:http_route path=/users method=GET
func (h *UserHandler) GetUsers() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Implementation
return httpx.JSON.OK(users), nil
})
}
POST Route
// goframe:http_route path=/users method=POST
func (h *UserHandler) CreateUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Implementation
return httpx.JSON.Created(user), nil
})
}
Multiple Methods
// goframe:http_route path=/items method=[GET,POST]
func (h *UserHandler) ManageItems() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Handle both GET and POST
})
}
Path Parameters
// goframe:http_route path=/users/{id} method=GET
func (h *UserHandler) GetUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Implementation
})
}
Generating the Router
Manual Generation
# Generate routes for all discovered handler packages
bin/goframe g router
# Generate routes for specific packages
bin/goframe g router [packages...]
The generator automatically discovers all root handler packages and their subfolders. A root package is identified by the presence of both router.go
and registry.go
files.
Generated Router Code
The system generates router registration code like:
func Router(p RouterParams) {
p.Mux.HandleFunc("GET /users", p.UserHandler.GetUsers())
p.Mux.HandleFunc("POST /users", p.UserHandler.CreateUser())
p.Mux.HandleFunc("GET /users/{id}", p.UserHandler.GetUser())
p.Mux.HandleFunc("GET /items", p.UserHandler.ManageItems())
p.Mux.HandleFunc("POST /items", p.UserHandler.ManageItems())
}
For handlers in subpackages, the generator automatically adds the appropriate import and uses the qualified name:
func Router(p RouterParams) {
p.Mux.HandleFunc("GET /users", p.UserHandler.GetUsers())
p.Mux.HandleFunc("GET /dashboard/stats", p.DashboardHandler.GetStats())
}
Hierarchical Package Structure
The generator supports a hierarchical package structure with root packages and subfolders:
Root Handler Packages
A root handler package contains both router.go
and registry.go
files and serves as the entry point for route registration.
Example Structure
internal/
├── v1handler/ # Root handler package
│ ├── router.go # Generated router with route registrations
│ ├── registry.go # Generated handler registry
│ ├── handler_user.go # Root level handlers
│ ├── handler_order.go
│ ├── profile/ # Subfolder with handlers
│ │ └── handler_profile.go
│ └── admin/ # Subfolder with handlers
│ ├── handler_admin.go
│ └── reports/ # Nested subfolder
│ └── handler_reports.go
└── v2handler/ # Another root handler package
├── router.go # Separate router for v2
├── registry.go # Separate registry for v2
└── handler_user.go
Package Discovery Rules
- Root packages: Must contain both
router.go
andregistry.go
- Subfolders: Contain only handler files, no
router.go
orregistry.go
- Auto-discovery: The generator automatically finds all root packages and their subfolders
- Import handling: Handlers from subfolders are automatically imported into the root package’s router
Complete Example
// handler_user.go
package v1handler
import (
"net/http"
"github.com/alexisvisco/goframe/http/httpx"
)
type UserHandler struct {
userService *UserService
}
// goframe:http_route path=/users method=GET
func (h *UserHandler) GetUsers() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
users, err := h.userService.GetAllUsers()
if err != nil {
return nil, err
}
return httpx.JSON.OK(users), nil
})
}
// goframe:http_route path=/users method=POST
func (h *UserHandler) CreateUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
// Parse request, create user
user, err := h.userService.CreateUser(name, email)
if err != nil {
return httpx.JSON.BadRequest(err), nil
}
return httpx.JSON.Created(user), nil
})
}
// goframe:http_route path=/users/{id} method=GET
func (h *UserHandler) GetUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
id := r.PathValue("id")
user, err := h.userService.GetUser(id)
if err != nil {
return httpx.JSON.NotFound(err), nil
}
return httpx.JSON.OK(user), nil
})
}
After running the generator, this creates a router with:
func Router(p RouterParams) {
p.Mux.HandleFunc("GET /users", p.UserHandler.GetUsers())
p.Mux.HandleFunc("POST /users", p.UserHandler.CreateUser())
p.Mux.HandleFunc("GET /users/{id}", p.UserHandler.GetUser())
}
Multi-Package Example
internal/
├── v1handler/ # Root package
│ ├── router.go
│ ├── registry.go
│ ├── handler_user.go
│ └── dashboard/ # Subfolder
│ └── handler_stats.go
└── adminhandler/ # Another root package
├── router.go
├── registry.go
└── handler_admin.go
This structure generates separate routers for each root package:
v1handler/router.go:
func Router(p RouterParams) {
p.Mux.HandleFunc("GET /users", p.UserHandler.GetUsers())
p.Mux.HandleFunc("GET /dashboard/stats", p.StatsHandler.GetStats())
}
adminhandler/router.go:
func Router(p RouterParams) {
p.Mux.HandleFunc("GET /admin/users", p.AdminHandler.GetUsers())
}
Benefits
- Organized codebase: Separate concerns into logical packages
- Automatic discovery: No manual package configuration required
- Scalable architecture: Support for complex hierarchical structures
- Multiple API versions: Each root package can represent different API versions
- Clean imports: Automatic import management for subpackage handlers