Routing
Overview
Routing maps HTTP requests to controller actions. The framework uses Gin router with organized route groups for different application sections.
Route Organization
Route Groups
// App routes for web interface
func (this *AppRouter) GardenRoutes() {
gardens := this.Router.Group("/gardens")
{
gardens.GET("/", this.GardenController.List)
gardens.GET("/new", this.GardenController.ShowNew)
gardens.POST("/", this.GardenController.Create)
gardens.GET("/:id", this.GardenController.Show)
gardens.GET("/:id/edit", this.GardenController.ShowEdit)
gardens.PUT("/:id", this.GardenController.Update)
gardens.DELETE("/:id", this.GardenController.Delete)
}
}
// API routes for JSON responses
func (this *ApiRouter) GardenRoutes() {
api := this.Router.Group("/api/v1")
gardens := api.Group("/gardens")
{
gardens.GET("/", this.GardenController.ApiList)
gardens.POST("/", this.GardenController.ApiCreate)
gardens.GET("/:id", this.GardenController.ApiShow)
gardens.PUT("/:id", this.GardenController.ApiUpdate)
gardens.DELETE("/:id", this.GardenController.ApiDelete)
}
}
Basic Route Patterns
RESTful Routes
// Standard CRUD patterns
GET /gardens -> List all gardens
GET /gardens/new -> Show create form
POST /gardens -> Create garden
GET /gardens/:id -> Show garden
GET /gardens/:id/edit -> Show edit form
PUT /gardens/:id -> Update garden
DELETE /gardens/:id -> Delete garden
Route Parameters
func (this *GardenController) Show(c *gin.Context) {
id := c.Param("id") // Extract :id parameter
garden, exists, err := this.GardenFetcher.FindOneById(c.Request.Context(), id, nil)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
if !exists {
c.JSON(404, gin.H{"error": "Garden not found"})
return
}
c.HTML(200, "gardens/show.html", gin.H{"garden": garden})
}
Query Parameters
func (this *GardenController) List(c *gin.Context) {
// Extract query parameters
search := c.Query("search") // ?search=tomato
page := c.DefaultQuery("page", "1") // ?page=2 (default 1)
// Build fetcher modification
mod := this.GardenFetcher.Mod()
if search != "" {
mod.ContainsStringValueFilter("name", search)
}
if pageInt, err := strconv.Atoi(page); err == nil {
mod.Page = pageInt
}
gardens, pagination, err := this.GardenFetcher.FindPage(c.Request.Context(), mod)
// ... render response
}
Route Registration
Router Setup
// src/app/routers/app_router.go
type AppRouter struct {
Router *gin.Engine
GardenController *controllers.GardenController
UserController *controllers.UserController
}
func (this *AppRouter) RegisterRoutes() {
// Register all route groups
this.GardenRoutes()
this.UserRoutes()
this.PlantRoutes()
// Static file serving
this.Router.Static("/assets", "./assets")
this.Router.StaticFile("/favicon.ico", "./assets/favicon.ico")
}
Middleware Application
func (this *AppRouter) RegisterRoutes() {
// Global middleware
this.Router.Use(middleware.Logger())
this.Router.Use(middleware.Recovery())
// Route group with authentication
protected := this.Router.Group("/")
protected.Use(middleware.Authentication())
{
protected.GET("/dashboard", this.DashboardController.Show)
protected.POST("/gardens", this.GardenController.Create)
}
// Public routes
this.Router.GET("/", this.HomeController.Index)
this.Router.GET("/login", this.AuthController.ShowLogin)
}
Controller Integration
Basic Controller Pattern
type GardenController struct {
GardenFetcher *fetchers.GardenFetcher
GardenSerializer *serializers.GardenSerializer
CreateGardenMae *mae.CreateGardenMae
GardenBinder *binders.GardenBinder
}
func (this *GardenController) Create(c *gin.Context) {
// 1. Bind form data
form := &forms.CreateGardenForm{}
if err := this.GardenBinder.BindCreateForm(c, form); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 2. Execute business logic
output, err := this.CreateGardenMae.Execute(c.Request.Context(), &maes.CreateGardenMaeInput{
Form: form,
})
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// 3. Return response
c.JSON(201, gin.H{"garden": output.Garden})
}
Response Types
HTML Responses
func (this *GardenController) Show(c *gin.Context) {
// Load garden data
garden := this.loadGarden(c)
// Render HTML template
c.HTML(200, "gardens/show.html", gin.H{
"title": "Garden Details",
"garden": garden,
})
}
JSON Responses
func (this *GardenController) ApiShow(c *gin.Context) {
garden := this.loadGarden(c)
// Serialize to JSON
jsonData, err := this.GardenSerializer.OneToJson(garden)
if err != nil {
c.JSON(500, gin.H{"error": "Serialization failed"})
return
}
c.Data(200, "application/json", jsonData)
}
Redirect Responses
func (this *GardenController) Update(c *gin.Context) {
// Update logic...
// Redirect after successful update
c.Redirect(302, fmt.Sprintf("/gardens/%s", garden.Id))
}
Common Route Patterns
Nested Resources
// Gardens with plants
func (this *AppRouter) GardenPlantRoutes() {
gardens := this.Router.Group("/gardens/:garden_id")
{
plants := gardens.Group("/plants")
{
plants.GET("/", this.PlantController.ListForGarden)
plants.POST("/", this.PlantController.CreateForGarden)
plants.GET("/:id", this.PlantController.Show)
}
}
}
Bulk Operations
// Bulk actions
func (this *ApiRouter) GardenBulkRoutes() {
api := this.Router.Group("/api/v1/gardens")
{
api.POST("/bulk", this.GardenController.BulkCreate)
api.PUT("/bulk", this.GardenController.BulkUpdate)
api.DELETE("/bulk", this.GardenController.BulkDelete)
}
}
Custom Actions
// Custom garden actions
func (this *AppRouter) GardenActionRoutes() {
gardens := this.Router.Group("/gardens/:id")
{
gardens.POST("/water", this.GardenController.Water)
gardens.POST("/harvest", this.GardenController.Harvest)
gardens.GET("/report", this.GardenController.GenerateReport)
}
}
Route Testing
Testing Route Handlers
func TestGardenController_Show(t *testing.T) {
// Setup
router := setupTestRouter()
controller := setupGardenController()
router.GET("/gardens/:id", controller.Show)
// Create request
req, _ := http.NewRequest("GET", "/gardens/123", nil)
w := httptest.NewRecorder()
// Execute
router.ServeHTTP(w, req)
// Assert
assert.Equal(t, 200, w.Code)
assert.Contains(t, w.Body.String(), "garden")
}
LLM Routing Notes
- Routes are organized in dedicated router files by feature
- Use route groups for logical organization and middleware application
- Follow RESTful conventions for CRUD operations
- Extract parameters with
c.Param()
andc.Query()
- Return appropriate HTTP status codes
- Test route handlers with HTTP test requests