Request Lifecycle
Overview
Understanding how requests flow through the framework is crucial for both debugging and development. This document traces the complete lifecycle of HTTP requests from arrival to response.
Request Flow Architecture
HTTP Request
↓
Gin Router (Middleware Stack)
↓
Route Handler
↓
Controller Method
↓
Binder (Form/JSON extraction)
↓
Mae (Business Logic)
↓
Services (Fetchers, Handlers, etc.)
↓
Database Operations
↓
Response Formation (View/JSON)
↓
HTTP Response
Detailed Request Lifecycle
1. HTTP Request Arrival
// Gin receives the HTTP request
// Example: POST /gardens
2. Middleware Processing
// Middleware stack executes in order
router.Use(middleware.Recovery())
router.Use(middleware.Logger())
router.Use(middleware.CORS())
router.Use(middleware.Authentication())
3. Route Matching
// Route resolution based on method and path
gardens.POST("/", gardenController.Create)
gardens.GET("/:id", gardenController.Show)
gardens.PUT("/:id", gardenController.Update)
4. Controller Execution
func (this *GardenController) Create(c *gin.Context) {
// Extract and bind form data
form := &forms.CreateGardenForm{}
if err := this.GardenBinder.BindCreateForm(c, form); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Execute business logic
output, err := this.CreateGardenMae.Execute(c.Request.Context(), &maes.CreateGardenMaeInput{
Form: form,
UserId: this.getCurrentUserId(c),
})
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Format response
c.JSON(201, gin.H{
"garden": output.Garden,
"success": true,
})
}
5. Mae Business Logic
func (this *CreateGardenMae) Execute(ctx context.Context, input *CreateGardenMaeInput) (*CreateGardenMaeOutput, error) {
// Step 1: Validate
if err := this.validateInput(input); err != nil {
return nil, err
}
// Step 2: Transform
garden := &mdl.Garden{}
garden, _ = this.GardenMolder.ToEntity(input.Form, garden)
// Step 3: Persist
createdGarden, err := this.GardenHandler.Create(ctx, garden, nil)
if err != nil {
return nil, err
}
return &CreateGardenMaeOutput{Garden: createdGarden}, nil
}
6. Service Layer Operations
// Handler creates the entity
func (this *GardenHandler) Create(ctx context.Context, entity *mdl.Garden, mod *handler.HandlerMod) (*mdl.Garden, error) {
// Set timestamps
entity.CreatedAt = this.Time.Now()
entity.UpdatedAt = this.Time.Now()
// Use Table service for database operation
values := this.GardenTable.EntityToValues(entity)
id, _, err := this.Handler.Create(ctx, mod.Tx, this.GardenTable.TableName(), this.GardenTable.Columns(), values)
if err != nil {
return nil, err
}
entity.Id = id
return entity, nil
}
7. Database Operations
// SQL execution through SqlDb abstraction
func (this *SqlDb) Create(ctx context.Context, tx *sql.Tx, table string, columns []string, values []interface{}) (string, int, error) {
query := this.buildInsertQuery(table, columns)
var result sql.Result
var err error
if tx != nil {
result, err = tx.ExecContext(ctx, query, values...)
} else {
result, err = this.db.ExecContext(ctx, query, values...)
}
if err != nil {
return "", 0, err
}
// Return generated ID
id, _ := result.LastInsertId()
return fmt.Sprintf("%d", id), 1, nil
}
8. Response Formation
// Controller formats final response
c.JSON(201, gin.H{
"garden": output.Garden,
"success": true,
})
// Or for HTML responses
c.HTML(200, "gardens/show.html", gin.H{
"garden": garden,
"title": "Garden Details",
})
Request Types and Patterns
CRUD Operations
Create Request Flow
POST /gardens
├── GardenController.Create
├── CreateGardenMae.Execute
├── GardenHandler.Create
├── GardenTable.EntityToValues
└── SqlDb.Create → Database
Read Request Flow
GET /gardens/123
├── GardenController.Show
├── GardenFetcher.FindOneById
├── GardenTable.EsoToEntity
├── GardenHydrator.OneViaPreset (optional)
└── Response (JSON/HTML)
Update Request Flow
PUT /gardens/123
├── GardenController.Update
├── UpdateGardenMae.Execute
├── GardenMolder.ToEntity (change detection)
├── GardenHandler.UpdateFields
├── GardenTable.EntityToValues
└── SqlDb.Update → Database
Delete Request Flow
DELETE /gardens/123
├── GardenController.Delete
├── DeleteGardenMae.Execute
├── GardenFetcher.FindOneById (verify exists)
├── GardenHandler.Delete
└── SqlDb.Delete → Database
List/Search Operations
GET /gardens?search=tomato&page=2
├── GardenController.List
├── GardenBinder.BindListParams
├── GardenFetcher.FindPage (with filters)
├── GardenHydrator.ManyViaPreset
├── GardenSerializer.SetToJson
└── JSON Response with pagination
Context Propagation
Request Context Flow
// Context flows through entire request
func (this *GardenController) Create(c *gin.Context) {
ctx := c.Request.Context() // Extract request context
// Pass to Mae
output, err := this.CreateGardenMae.Execute(ctx, input)
}
func (this *CreateGardenMae) Execute(ctx context.Context, input *CreateGardenMaeInput) {
// Pass to services
garden, err := this.GardenHandler.Create(ctx, entity, nil)
}
func (this *GardenHandler) Create(ctx context.Context, entity *mdl.Garden, mod *handler.HandlerMod) {
// Pass to database
id, _, err := this.Handler.Create(ctx, mod.Tx, table, columns, values)
}
Context Values
// Adding values to context
func (this *AuthMiddleware) Authenticate(c *gin.Context) {
user := this.validateToken(c.GetHeader("Authorization"))
// Add user to context
ctx := context.WithValue(c.Request.Context(), "user", user)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
// Retrieving from context
func (this *GardenController) getCurrentUser(c *gin.Context) *mdl.User {
user, _ := c.Request.Context().Value("user").(*mdl.User)
return user
}
Error Handling Through Lifecycle
Error Propagation
// Errors bubble up through layers
Database Error
↑
Service Error (with context)
↑
Mae Error (with business context)
↑
Controller Error Handling
↑
HTTP Error Response
Error Response Formation
func (this *GardenController) Create(c *gin.Context) {
output, err := this.CreateGardenMae.Execute(c.Request.Context(), input)
if err != nil {
// Determine error type and appropriate response
switch e := err.(type) {
case *ValidationError:
c.JSON(400, gin.H{
"error": e.Message,
"field": e.Field,
})
case *BusinessRuleError:
c.JSON(422, gin.H{
"error": e.Message,
"code": e.Code,
})
default:
c.JSON(500, gin.H{
"error": "Internal server error",
})
}
return
}
c.JSON(201, gin.H{"garden": output.Garden})
}
Transaction Lifecycle
Transaction Boundaries
func (this *ComplexGardenMae) Execute(ctx context.Context, input *ComplexGardenMaeInput) (*ComplexGardenMaeOutput, error) {
// Begin transaction
tx, err := this.SqlDb.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback() // Auto-rollback if not committed
// All operations use the same transaction
handlerMod := &handler.HandlerMod{Tx: tx}
// Create garden
garden, err := this.GardenHandler.Create(ctx, gardenEntity, handlerMod)
if err != nil {
return nil, err
}
// Create related plants
for _, plant := range input.Plants {
plant.GardenId = garden.Id
_, err := this.PlantHandler.Create(ctx, plant, handlerMod)
if err != nil {
return nil, err
}
}
// Commit transaction
if err := tx.Commit(); err != nil {
return nil, err
}
return &ComplexGardenMaeOutput{Garden: garden}, nil
}
Performance Monitoring Points
Request Timing
func (this *GardenController) Create(c *gin.Context) {
start := time.Now()
defer func() {
duration := time.Since(start)
this.Metrics.RecordRequestDuration("POST", "/gardens", duration)
}()
// ... request processing
}
Service Operation Timing
func (this *GardenHandler) Create(ctx context.Context, entity *mdl.Garden, mod *handler.HandlerMod) (*mdl.Garden, error) {
this.Spy.LogOperation(ctx, "GardenHandler.Create", map[string]interface{}{
"garden_name": entity.Name,
"user_id": entity.UserId,
})
// ... implementation
}
Request Lifecycle Debugging
Tracing Requests
// Add request ID to context
func (this *TracingMiddleware) AddRequestID(c *gin.Context) {
requestId := uuid.New().String()
ctx := context.WithValue(c.Request.Context(), "request_id", requestId)
c.Request = c.Request.WithContext(ctx)
c.Header("X-Request-ID", requestId)
c.Next()
}
// Log with request ID
func (this *Logger) LogWithContext(ctx context.Context, level, message string, fields ...interface{}) {
requestId, _ := ctx.Value("request_id").(string)
this.Log(level, message, append(fields, "request_id", requestId)...)
}
Request State Inspection
// Middleware for request state logging
func (this *DebugMiddleware) LogRequestState(c *gin.Context) {
if this.Config.Environment == "dev" {
this.Logger.Debug("Request received",
"method", c.Request.Method,
"path", c.Request.URL.Path,
"headers", c.Request.Header,
)
}
c.Next()
if this.Config.Environment == "dev" {
this.Logger.Debug("Response sent",
"status", c.Writer.Status(),
"size", c.Writer.Size(),
)
}
}
LLM Request Lifecycle Notes
When debugging or developing: 1. Request Context - Always propagate context through all layers 2. Error Handling - Handle errors at appropriate levels with context 3. Transaction Management - Use transactions for multi-entity operations 4. Performance Monitoring - Add timing and logging at key points 5. Debugging - Use middleware for request tracing and state inspection 6. Service Integration - Understand the flow through Mae → Services → Database 7. Response Formation - Handle both success and error cases consistently