URL Helper Generation Documentation
Overview
The goframe URL helper generator automatically creates type-safe URL building functions based on your route definitions. It analyzes your handlers and their request types across all handler packages to generate functions that help you construct URLs with proper path parameters and query strings.
How It Works
The system works in four main steps:
- Package Discovery: Automatically discovers root handler packages and their subfolders
- Route Analysis: Scans existing routes from
goframe:http_route
annotations across all packages - Request Type Introspection: Analyzes request structs for
path
andquery
field tags - Code Generation: Generates organized URL helper functions with proper namespacing
Request Type Tags
Define request structs with field tags to specify path and query parameters:
type GetUserRequest struct {
ID string `path:"id"` // Path parameter
Format string `query:"format"` // Query parameter
Filter string `query:"filter"` // Query parameter
}
// goframe:http_route path=/users/{id} method=GET request=GetUserRequest
func (h *UserHandler) GetUser() http.HandlerFunc {
return httpx.Wrap(func(r *http.Request) (httpx.Response, error) {
id := r.PathValue("id")
format := r.URL.Query().Get("format")
// Implementation
})
}
Generated URL Helper Structure
The generator creates a structured URL helper with namespaces based on handler names:
package urlhelper
import (
"fmt"
"net/url"
"strings"
"your-app/config"
)
type URLs struct {
baseURL string
}
func NewURLs(c *config.Config) URLs {
return URLs{
baseURL: c.GetServer().URL,
}
}
// Namespace accessors
func (u URLs) User() UserURL {
return UserURL{baseURL: u.baseURL}
}
func (u URLs) Order() OrderURL {
return OrderURL{baseURL: u.baseURL}
}
Generated URL Functions
Simple Routes (No Parameters)
// From: goframe:http_route path=/users method=GET
func (u UserURL) GetUsers() string {
path := "/users"
return u.baseURL + path
}
Routes with Path Parameters
// From: goframe:http_route path=/users/{id} method=GET
// Request type has: ID string `path:"id"`
func (u UserURL) GetUser(id string) string {
path := "/users/{id}"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(fmt.Sprint(id)))
return u.baseURL + path
}
Routes with Query Parameters
// Request type has: Format string `query:"format"`, Page string `query:"page"`
func (u UserURL) SearchUsers(format string, page string) string {
path := "/users/search"
q := url.Values{}
if format != "" { q.Set("format", fmt.Sprint(format)) }
if page != "" { q.Set("page", fmt.Sprint(page)) }
if enc := q.Encode(); enc != "" { path += "?" + enc }
return u.baseURL + path
}
Routes with Both Path and Query Parameters
// Request type has: ID string `path:"id"`, Include string `query:"include"`
func (u UserURL) GetUserDetails(id string, include string) string {
path := "/users/{id}/details"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(fmt.Sprint(id)))
q := url.Values{}
if include != "" { q.Set("include", fmt.Sprint(include)) }
if enc := q.Encode(); enc != "" { path += "?" + enc }
return u.baseURL + path
}
Complete Example
Handler Definitions
// handler_user.go
package v1handler
type UserHandler struct {
userService *UserService
}
type GetUserRequest struct {
ID string `path:"id"`
}
type SearchUsersRequest struct {
Query string `query:"q"`
Limit string `query:"limit"`
Offset string `query:"offset"`
}
type UpdateUserRequest struct {
ID string `path:"id"`
Name string `json:"name"`
}
// goframe:http_route path=/users method=GET request=SearchUsersRequest
func (h *UserHandler) GetUsers() http.HandlerFunc { /* ... */ }
// goframe:http_route path=/users/{id} method=GET request=GetUserRequest
func (h *UserHandler) GetUser() http.HandlerFunc { /* ... */ }
// goframe:http_route path=/users/{id} method=PUT request=UpdateUserRequest
func (h *UserHandler) UpdateUser() http.HandlerFunc { /* ... */ }
Generated URL Helper
// internal/v1handler/urlhelper/urlhelper.go
package urlhelper
import (
"fmt"
"net/url"
"strings"
"your-app/config"
)
type URLs struct {
baseURL string
}
func NewURLs(c *config.Config) URLs {
return URLs{
baseURL: c.GetServer().URL,
}
}
func (u URLs) User() UserURL {
return UserURL{baseURL: u.baseURL}
}
type UserURL struct {
baseURL string
}
func (u UserURL) GetUsers(query string, limit string, offset string) string {
path := "/users"
q := url.Values{}
if query != "" { q.Set("q", fmt.Sprint(query)) }
if limit != "" { q.Set("limit", fmt.Sprint(limit)) }
if offset != "" { q.Set("offset", fmt.Sprint(offset)) }
if enc := q.Encode(); enc != "" { path += "?" + enc }
return u.baseURL + path
}
func (u UserURL) GetUser(id string) string {
path := "/users/{id}"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(fmt.Sprint(id)))
return u.baseURL + path
}
func (u UserURL) UpdateUser(id string) string {
path := "/users/{id}"
path = strings.ReplaceAll(path, "{id}", url.PathEscape(fmt.Sprint(id)))
return u.baseURL + path
}
Usage in Application Code
Initialize URL Helper
func main() {
...
urls := urlhelper.NewURLs(cfg)
// Use the URL helper
userListURL := urls.User().GetUsers("john", "10", "0")
// Returns: "https://api.example.com/users?q=john&limit=10&offset=0"
userURL := urls.User().GetUser("123")
// Returns: "https://api.example.com/users/123"
}
Function Name Resolution
The generator handles naming conflicts intelligently:
- Base name: Uses the method name (e.g.,
GetUser
) - Method conflict: Adds HTTP method prefix (e.g.,
GetGetUser
,PostGetUser
) - Path conflict: Adds path segments (e.g.,
GetUsersIdGetUser
)
Namespace Organization
Functions are organized by handler name with automatic package prefixing:
Root Package Handlers
UserHandler
ininternal/v1handler
→UserURL
namespace →urls.User().MethodName()
OrderHandler
ininternal/v1handler
→OrderURL
namespace →urls.Order().MethodName()
Subpackage Handlers
UserHandler
ininternal/v1handler/admin
→AdminUserURL
namespace →urls.AdminUser().MethodName()
StatsHandler
ininternal/v1handler/dashboard
→DashboardStatsURL
namespace →urls.DashboardStats().MethodName()
Multiple Root Packages
UserHandler
ininternal/v2handler
→UserURL
namespace (separate URL helper file)- Functions without handlers →
RootURL
namespace →urls.Root().MethodName()
The namespace automatically includes the package hierarchy to prevent conflicts between handlers with the same name in different packages.
Generate URL Helpers
# Generate URL helpers for all discovered handler packages
bin/goframe g url-helper
# Generate URL helpers for specific packages
bin/goframe g url-helper [packages...]
The generator automatically discovers all root handler packages (containing router.go
and registry.go
) and their subfolders.
Hierarchical Package Example
internal/
├── v1handler/ # Root package
│ ├── router.go
│ ├── registry.go
│ ├── urlhelper/
│ │ └── urlhelper.go # Generated URL helpers
│ ├── handler_user.go # → UserURL namespace
│ ├── handler_order.go # → OrderURL namespace
│ ├── admin/ # Subpackage
│ │ └── handler_admin.go # → AdminAdminURL namespace
│ └── dashboard/ # Subpackage
│ └── handler_stats.go # → DashboardStatsURL namespace
└── v2handler/ # Another root package
├── router.go
├── registry.go
├── urlhelper/
│ └── urlhelper.go # Separate URL helpers for v2
└── handler_user.go # → UserURL namespace (in v2 context)
Generated URL helper structure:
// internal/v1handler/urlhelper/urlhelper.go
func (u URLs) User() UserURL { ... } // v1handler/handler_user.go
func (u URLs) Order() OrderURL { ... } // v1handler/handler_order.go
func (u URLs) AdminAdmin() AdminAdminURL { ... } // v1handler/admin/handler_admin.go
func (u URLs) DashboardStats() DashboardStatsURL { ... } // v1handler/dashboard/handler_stats.go
Benefits
- Type Safety: Compile-time checking of URL parameters
- DRY Principle: Single source of truth for URL patterns
- Refactoring Safe: URL changes automatically update helper functions
- IDE Support: Auto-completion and parameter hints
- URL Encoding: Automatic proper encoding of path and query parameters
- Base URL Management: Centralized base URL configuration
- Package Organization: Clear namespace separation for different packages
- Conflict Resolution: Automatic prefixing prevents naming conflicts