# Binders

## Overview

Binders are responsible for extracting and binding data from HTTP requests (query parameters, form data, JSON bodies) into structured input objects for Maestro operations. They provide a clean interface between raw HTTP data and typed Go structures.

## Purpose

Binders serve several key functions:
- **Data Extraction** - Parse HTTP requests and extract relevant data
- **Type Conversion** - Convert string values to appropriate Go types
- **Input Preparation** - Populate Maestro input structures with bound data
- **Request Context** - Handle request-specific context and metadata

## Structure

Each binder typically follows this pattern:

```go
type NewUserAppBinder struct {
    Binder *binder.Binder
}

func (this *NewUserAppBinder) Bind(ctx context.Context, in *maes.NewUserMaeIn, c *gin.Context) error {
    this.bindRequest(ctx, in, c)
    return nil
}
```

## Common Binding Methods

### Query String Binding
```go
func (this *SomeBinder) bindRequestForm(ctx context.Context, in *maes.SomeMaeIn, c *gin.Context) error {
    s := &in.Request.Form
    s.Name = this.Binder.QueryStringField("form[name]", c, &in.Context)
    s.Email = this.Binder.QueryStringField("form[email]", c, &in.Context)
    return nil
}
```

### JSON Body Binding
```go
func (this *SomeBinder) bindRequestJSON(ctx context.Context, in *maes.SomeMaeIn, c *gin.Context) error {
    var jsonData struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }

    if err := c.ShouldBindJSON(&jsonData); err != nil {
        return err
    }

    in.Request.Form.Name = jsonData.Name
    in.Request.Form.Email = jsonData.Email
    return nil
}
```

### Form Data Binding
```go
func (this *SomeBinder) bindRequestForm(ctx context.Context, in *maes.SomeMaeIn, c *gin.Context) error {
    s := &in.Request.Form
    s.Name = c.PostForm("name")
    s.Email = c.PostForm("email")
    return nil
}
```

## Integration with Maestros

Binders are typically called at the beginning of controller methods:

```go
func (gc *GardenController) Create(c *gin.Context) {
    input := &maes.CreateGardenMaeIn{}

    // Bind request data
    if err := gc.CreateGardenBinder.Bind(c.Request.Context(), input, c); err != nil {
        c.JSON(400, gin.H{"error": "Invalid request data"})
        return
    }

    // Execute business logic
    output, err := gc.CreateGardenMae.Execute(c.Request.Context(), input)
    // ...
}
```

## Request Context Handling

Binders also populate request context information:

```go
func (this *SomeBinder) bindContext(ctx context.Context, in *maes.SomeMaeIn, c *gin.Context) error {
    // Extract user context
    if userID, exists := c.Get("user_id"); exists {
        in.Context.UserId = userID.(string)
    }

    // Set request metadata
    in.Context.RequestID = c.GetString("request_id")
    in.Context.Timestamp = time.Now()
    return nil
}
```

## Error Handling

Binders should handle binding errors gracefully:

```go
func (this *SomeBinder) Bind(ctx context.Context, in *maes.SomeMaeIn, c *gin.Context) error {
    if err := this.bindRequest(ctx, in, c); err != nil {
        return fmt.Errorf("failed to bind request: %w", err)
    }

    if err := this.validateBindings(in); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }

    return nil
}
```

## Best Practices

### Type Safety
- Always convert string inputs to appropriate types
- Handle conversion errors gracefully
- Provide default values when appropriate

### Validation
- Perform basic format validation during binding
- Leave business rule validation to validators
- Return clear error messages for binding failures

### Performance
- Bind only necessary data
- Avoid expensive operations in binders
- Cache parsed values when appropriate

### Security
- Sanitize input data
- Validate parameter names and values
- Prevent parameter pollution attacks

## Testing Binders

```go
func TestCreateGardenBinder(t *testing.T) {
    binder := setupCreateGardenBinder()

    // Setup mock request
    c, _ := gin.CreateTestContext(httptest.NewRecorder())
    c.Request = httptest.NewRequest("POST", "/gardens", strings.NewReader(`{"name": "Test Garden"}`))
    c.Request.Header.Set("Content-Type", "application/json")

    input := &maes.CreateGardenMaeIn{}
    err := binder.Bind(context.Background(), input, c)

    assert.NoError(t, err)
    assert.Equal(t, "Test Garden", input.Request.Form.Name)
}
```

Binders provide the crucial first step in request processing, ensuring that raw HTTP data is properly structured and typed for use throughout the application.