# Implementation Lokstra Create Handler

> Create @Handler annotated HTTP endpoint services. Generate handler structs with @Route decorators, dependency injection, request/response handling, and auto-validation. Use after API specifications are approved to implement business logic endpoints. Use when this capability is needed.

- Skill: `tomevault-io/implementation-lokstra-create-handler` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/implementation-lokstra-create-handler`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/implementation-lokstra-create-handler/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/implementation-lokstra-create-handler

---


# Implementation: @Handler Creation

## When to Use

Use this skill when:
- Implementing HTTP REST endpoints from API specification
- Creating handler services with multiple routes
- Setting up dependency injection for handlers
- Adding automatic request validation and error handling
- Building CRUD operations with path parameters

Prerequisites:
- ✅ API specification finalized (see: design-lokstra-api-specification)
- ✅ Domain models created (DTOs with validation tags)
- ✅ Repository interfaces defined (see: design-lokstra-module-requirements)
- ✅ config.yaml with service definitions ready (see: implementation-lokstra-yaml-config)
- ✅ Framework initialized in main.go (see: implementation-lokstra-init-framework)

## Basic Handler Structure

### Example: User Handler

**File:** `modules/user/application/user_handler.go`

```go
package application

import (
	"myapp/modules/user/domain"
	"github.com/primadi/lokstra/core/request"
)

// @Handler annotation defines:
// - name: unique service identifier for DI
// - prefix: base URL path for all routes in this handler
// - middlewares (optional): global middlewares for all routes
//
// @Handler name="user-handler", prefix="/api/users"
type UserHandler struct {
	// @Inject dependency injection
	// References service name from config.yaml or another @Service
	//
	// @Inject "user-repository"
	UserRepo domain.UserRepository

	// @Inject config values using "cfg:" prefix
	// @Inject "cfg:app.timeout"
	Timeout time.Duration
}

// @Route "GET /{id}"
// Returns user or error (Lokstra auto-converts to JSON response)
func (h *UserHandler) GetByID(id string) (*domain.User, error) {
	return h.UserRepo.GetByID(id)
}

// @Route "GET /"
// List all users
func (h *UserHandler) List() ([]*domain.User, error) {
	return h.UserRepo.List()
}

// @Route "POST /", middlewares=["auth"]
// Create user with request validation
// params are auto-validated based on struct tags
func (h *UserHandler) Create(params *domain.CreateUserRequest) (*domain.User, error) {
	user := &domain.User{
		Name:  params.Name,
		Email: params.Email,
	}
	return h.UserRepo.Create(user)
}

// @Route "PUT /{id}", middlewares=["auth"]
// Update user with path parameter and request body
func (h *UserHandler) Update(id string, params *domain.UpdateUserRequest) (*domain.User, error) {
	user, err := h.UserRepo.GetByID(id)
	if err != nil {
		return nil, err
	}
	
	user.Name = params.Name
	user.Email = params.Email
	return h.UserRepo.Update(user)
}

// @Route "DELETE /{id}", middlewares=["auth", "admin"]
// Delete user (admin only)
func (h *UserHandler) Delete(id string) error {
	return h.UserRepo.Delete(id)
}
```

**File:** `modules/user/domain/user_dto.go`

```go
package domain

// Request DTOs with validation tags
type CreateUserRequest struct {
	Name  string `json:"name" validate:"required,min=3,max=50"`
	Email string `json:"email" validate:"required,email"`
	Age   int    `json:"age" validate:"omitempty,min=0,max=150"`
}

type UpdateUserRequest struct {
	Name  string `json:"name" validate:"required,min=3,max=50"`
	Email string `json:"email" validate:"required,email"`
}

// Domain model
type User struct {
	ID        string    `json:"id"`
	Name      string    `json:"name"`
	Email     string    `json:"email"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}
```

---

## Handler Components Deep Dive

### 1. @Handler Annotation

Defines the handler service and its configuration:

```go
// Basic handler
// @Handler name="user-handler", prefix="/api/users"
type UserHandler struct { }

// With global middlewares
// @Handler name="admin-handler", prefix="/api/admin", middlewares=["auth", "admin"]
type AdminHandler struct { }

// Multiple handlers in same module
// @Handler name="user-public-handler", prefix="/api/public/users"
type UserPublicHandler struct { }

// @Handler name="user-admin-handler", prefix="/api/admin/users", middlewares=["auth", "admin"]
type UserAdminHandler struct { }
```

**Annotation Parameters:**

| Parameter | Required | Description | Example |
|-----------|----------|-------------|---------|
| `name` | Yes | Unique service identifier for DI | `"user-handler"` |
| `prefix` | Yes | Base URL path for all routes | `"/api/users"` |
| `middlewares` | No | Global middlewares for all routes | `["auth", "logger"]` |

**Key Rules:**
- Handler names must be unique across the application
- Prefix should follow REST conventions (`/api/resource`)
- Middlewares are applied to ALL routes in this handler
- Use empty prefix `prefix=""` to mount routes at root

### 2. @Inject Dependency Injection

Inject services and configuration into handler fields:

```go
type UserHandler struct {
	// Pattern 1: Direct service injection
	// @Inject "user-repository"
	UserRepo domain.UserRepository

	// Pattern 2: Service from config (interface selection)
	// References service name from config.yaml
	// @Inject "@repositories.user"
	UserRepo domain.UserRepository

	// Pattern 3: Config value injection
	// @Inject "cfg:app.timeout"
	Timeout time.Duration
	
	// @Inject "cfg:app.name"
	AppName string
	
	// @Inject "cfg:features.email_enabled"
	EmailEnabled bool

	// Pattern 4: Indirect config reference
	// Looks up value at key specified in another config
	// @Inject "cfg:@database.connection_string"
	DatabaseURL string
	
	// Multiple injections
	// @Inject "logger-service"
	Logger domain.Logger
	
	// @Inject "cache-service"
	Cache domain.CacheService
}
```

**Injection Patterns:**

| Pattern | Syntax | Use Case | Example |
|---------|--------|----------|---------|
| Direct service | `"service-name"` | Inject registered service | `"user-repository"` |
| Config service ref | `"@config.key"` | Service name from config | `"@repository.impl"` |
| Config value | `"cfg:key"` | Direct config value | `"cfg:app.timeout"` |
| Indirect config | `"cfg:@key"` | Config value reference | `"cfg:@jwt.secret_path"` |

### 3. @Route HTTP Endpoints

Define HTTP methods and paths for handler functions:

```go
// Basic routes
// @Route "GET /"                         // List all
// @Route "GET /{id}"                     // Get by ID
// @Route "POST /"                        // Create
// @Route "PUT /{id}"                     // Update
// @Route "PATCH /{id}"                   // Partial update
// @Route "DELETE /{id}"                  // Delete

// With route-specific middlewares
// @Route "POST /", middlewares=["auth"]
// @Route "DELETE /{id}", middlewares=["auth", "admin"]

// Multiple path parameters
// @Route "GET /{userId}/posts/{postId}"
// @Route "PUT /{teamId}/members/{memberId}"

// Complex paths
// @Route "POST /{userId}/activate"
// @Route "POST /{userId}/suspend"
// @Route "GET /{id}/export"

// Query parameters (use struct with query tags)
// @Route "GET /search"
func (h *UserHandler) Search(params *SearchParams) ([]*User, error) { }
```

**Route Parameters:**

| Component | Description | Example |
|-----------|-------------|---------|
| HTTP Method | GET, POST, PUT, PATCH, DELETE | `"GET"` |
| Path | URL path (relative to handler prefix) | `"/{id}"` |
| Path params | `{paramName}` in path | `"/{userId}/posts/{postId}"` |
| Middlewares | Route-specific middleware list | `middlewares=["auth", "rate-limit"]` |

**Important Notes:**
- Paths are relative to handler's `prefix`
- Path parameters must match function parameter names
- Middlewares in `@Route` are added to handler's global middlewares
- Route methods generate: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `HEAD`

---

## Handler Function Signatures

Lokstra supports **29+ handler signatures**. Choose the signature that best fits your needs.

### 1. Simple Return (No Parameters)

```go
// @Route "GET /ping"
func (h *UserHandler) Ping() string {
	return "pong"
}

// @Route "GET /status"
func (h *UserHandler) Status() map[string]interface{} {
	return map[string]interface{}{
		"status": "healthy",
		"timestamp": time.Now(),
	}
}
```

**Use Case:** Simple endpoints, health checks, static responses

### 2. Return with Error

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) {
	user, err := h.UserRepo.GetByID(id)
	if err != nil {
		return nil, fmt.Errorf("user not found: %w", err)
	}
	return user, nil
}

// @Route "GET /"
func (h *UserHandler) List() ([]*domain.User, error) {
	return h.UserRepo.List()
}
```

**Use Case:** Most common pattern, automatic JSON response, error handling

### 3. Request Body with Auto-Validation

```go
// @Route "POST /"
func (h *UserHandler) Create(req *domain.CreateUserRequest) (*domain.User, error) {
	// req is already validated by framework
	user := &domain.User{
		Name:  req.Name,
		Email: req.Email,
	}
	return h.UserRepo.Create(user)
}
```

**Validation Tags (in DTO):**

```go
type CreateUserRequest struct {
	Name     string `json:"name" validate:"required,min=3,max=50"`
	Email    string `json:"email" validate:"required,email"`
	Age      int    `json:"age" validate:"omitempty,min=0,max=150"`
	Website  string `json:"website" validate:"omitempty,url"`
	Password string `json:"password" validate:"required,min=8"`
}
```

**Use Case:** POST/PUT endpoints with request body validation

### 4. Path Parameters

```go
// Single parameter
// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) {
	return h.UserRepo.GetByID(id)
}

// Multiple parameters
// @Route "PUT /{id}"
func (h *UserHandler) Update(id string, req *domain.UpdateUserRequest) (*domain.User, error) {
	user, err := h.UserRepo.GetByID(id)
	if err != nil {
		return nil, err
	}
	user.Name = req.Name
	user.Email = req.Email
	return h.UserRepo.Update(user)
}

// Multiple path parameters
// @Route "GET /{userId}/posts/{postId}"
func (h *UserHandler) GetPost(userId, postId string) (*domain.Post, error) {
	return h.PostRepo.GetByUserAndPost(userId, postId)
}
```

**Use Case:** RESTful CRUD operations with resource identifiers

### 5. With Context (Custom Response Control)

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(ctx *request.Context, id string) error {
	if id == "" {
		return ctx.Api.BadRequest("id is required")
	}
	
	user, err := h.UserRepo.GetByID(id)
	if err != nil {
		return ctx.Api.NotFound("user not found")
	}
	
	return ctx.Api.Ok(user)
}

// @Route "POST /"
func (h *UserHandler) Create(ctx *request.Context, req *domain.CreateUserRequest) error {
	user := &domain.User{
		Name:  req.Name,
		Email: req.Email,
	}
	
	saved, err := h.UserRepo.Create(user)
	if err != nil {
		return ctx.Api.InternalServerError("failed to create user")
	}
	
	// Return 201 Created
	return ctx.Api.Created(saved)
}

// @Route "DELETE /{id}"
func (h *UserHandler) Delete(ctx *request.Context, id string) error {
	err := h.UserRepo.Delete(id)
	if err != nil {
		return ctx.Api.InternalServerError("failed to delete user")
	}
	
	// Return 204 No Content
	return ctx.Api.NoContent()
}
```

**Available Response Helpers:**

```go
ctx.Api.Ok(data)                      // 200 OK
ctx.Api.Created(data)                 // 201 Created
ctx.Api.NoContent()                   // 204 No Content
ctx.Api.BadRequest(message)           // 400 Bad Request
ctx.Api.Unauthorized(message)         // 401 Unauthorized
ctx.Api.Forbidden(message)            // 403 Forbidden
ctx.Api.NotFound(message)             // 404 Not Found
ctx.Api.Conflict(message)             // 409 Conflict
ctx.Api.InternalServerError(message)  // 500 Internal Server Error
```

**Use Case:** Custom HTTP status codes, advanced error handling, accessing request context

### 6. Query Parameters

```go
// Define struct with query tags
type SearchParams struct {
	Query  string `query:"q" validate:"required"`
	Page   int    `query:"page" validate:"min=1"`
	Limit  int    `query:"limit" validate:"min=1,max=100"`
	SortBy string `query:"sort" validate:"omitempty,oneof=name email created_at"`
}

// @Route "GET /search"
func (h *UserHandler) Search(params *SearchParams) ([]*domain.User, error) {
	return h.UserRepo.Search(params.Query, params.Page, params.Limit, params.SortBy)
}

// With context
// @Route "GET /search"
func (h *UserHandler) SearchWithContext(ctx *request.Context, params *SearchParams) error {
	users, err := h.UserRepo.Search(params.Query, params.Page, params.Limit, params.SortBy)
	if err != nil {
		return ctx.Api.InternalServerError("search failed")
	}
	return ctx.Api.Ok(users)
}
```

**Use Case:** Search endpoints, filtering, pagination

### 7. Combined Parameters (Context + Path + Body)

```go
// @Route "PUT /{id}"
func (h *UserHandler) Update(ctx *request.Context, id string, req *domain.UpdateUserRequest) error {
	// Validate ID
	if id == "" {
		return ctx.Api.BadRequest("id is required")
	}
	
	// Check existence
	existing, err := h.UserRepo.GetByID(id)
	if err != nil {
		return ctx.Api.NotFound("user not found")
	}
	
	// Update fields
	existing.Name = req.Name
	existing.Email = req.Email
	
	// Save
	updated, err := h.UserRepo.Update(existing)
	if err != nil {
		return ctx.Api.InternalServerError("update failed")
	}
	
	return ctx.Api.Ok(updated)
}

// @Route "POST /{teamId}/members"
func (h *TeamHandler) AddMember(ctx *request.Context, teamId string, req *domain.AddMemberRequest) error {
	member, err := h.TeamRepo.AddMember(teamId, req.UserID, req.Role)
	if err != nil {
		return ctx.Api.InternalServerError("failed to add member")
	}
	return ctx.Api.Created(member)
}
```

**Use Case:** Complex operations requiring fine-grained control

---

## request.Context: Abstraction Layers

The `request.Context` is the core API for handler functions. It provides multiple layers of abstraction over Go's standard HTTP primitives, allowing you to choose the level of abstraction that fits your needs.

### Architecture: Four-Layer Abstraction

```
Layer 4 (Business Logic)
    ↓
ctx.Api              // Opinionated API responses (wrapped in standard format)
    ↓
Layer 3 (Structured Responses)
    ↓
ctx.Resp             // Generic response builder (JSON, HTML, streaming)
    ↓
Layer 2 (Low-Level HTTP)
    ↓
ctx.W                // ResponseWriter  |  ctx.R.Method, ctx.R.URL, etc.
    ↓
Layer 1 (Go Standard Library)
    ↓
http.ResponseWriter  |  http.Request
```

### Context Fields Overview

```go
type Context struct {
	context.Context        // Embedded - standard Go context (for transactions, values)
	
	// Layer 4: High-level, opinionated API responses
	Api  *ApiHelper        // Best for: REST APIs, standard response format
	
	// Layer 3: Generic, low-level response builder
	Resp *Response         // Best for: Custom responses, non-JSON formats
	
	// Layer 2: Request/Response primitives + helpers
	Req  *RequestHelper    // Request data binding & validation (high-level)
	W    *writerWrapper    // ResponseWriter wrapper (raw HTTP output)
	R    *http.Request     // Go's standard http.Request (raw HTTP input)
}
```

---

## Layer 1: Go Standard Library

Raw HTTP primitives from Go's standard library. Use when you need maximum control.

### http.Request (ctx.R)

Access the raw HTTP request:

```go
// Basic information
method := ctx.R.Method                              // "GET", "POST", etc.
url := ctx.R.URL.String()                           // Full request URL
path := ctx.R.URL.Path                              // Just the path
query := ctx.R.URL.Query()                          // Query parameters as map
headers := ctx.R.Header                             // All HTTP headers

// Specific values
contentType := ctx.R.Header.Get("Content-Type")
userAgent := ctx.R.Header.Get("User-Agent")

// Request body (use ctx.Req.RawRequestBody() instead)
body := ctx.R.Body

// Client address
remoteAddr := ctx.R.RemoteAddr                      // "192.168.1.1:12345"

// Standard context (for database, timeouts, cancellation)
goContext := ctx.R.Context()                        // context.Context
```

### http.ResponseWriter (ctx.W)

Write raw HTTP responses (use ctx.Resp or ctx.Api instead):

```go
// ⚠️ Low-level API - prefer ctx.Resp or ctx.Api
ctx.W.Header().Set("X-Custom-Header", "value")      // Set header
ctx.W.WriteHeader(http.StatusCreated)               // Set status code
ctx.W.Write([]byte("raw data"))                     // Write body
```

---

## Layer 2: Request/Response Helpers (High-Level Primitives)

Type-safe, validated access to request data and response building.

### RequestHelper (ctx.Req)

Extract and bind request data with validation:

```go
// Single value getters (safe, with defaults)
value := ctx.Req.QueryParam("q", "")               // Query: ?q=value
value := ctx.Req.PathParam("id", "")               // Path: /users/{id}
value := ctx.Req.HeaderParam("X-API-Key", "")      // Header: X-API-Key
value := ctx.Req.FormParam("field", "")            // Form field

// Multiple value getters
values := ctx.Req.QueryParams("tags")              // []string - multiple query values
values := ctx.Req.HeaderValues("Accept")           // []string - header values

// All at once
all := ctx.Req.AllQueryParams()                    // map[string][]string
all := ctx.Req.AllHeaders()                        // map[string][]string

// Raw request body
body, err := ctx.Req.RawRequestBody()              // []byte + error

// Binding with automatic validation
err := ctx.Req.BindPath(&params)                   // Bind path params + validate
err := ctx.Req.BindQuery(&params)                  // Bind query params + validate
err := ctx.Req.BindHeader(&params)                 // Bind headers + validate
err := ctx.Req.BindBody(&params)                   // Bind JSON body + validate
err := ctx.Req.BindAll(&params)                    // Bind all sources + validate
err := ctx.Req.BindAllAuto(&params)                // Auto-detect content-type + validate
```

**Example:**

```go
// @Route "GET /users"
func (h *UserHandler) List(params *ListUsersRequest) ([]*User, error) {
	// params auto-bound and validated from:
	// - Query: ?page=1&limit=10&status=active
	// - Already validated by framework before entering handler
	
	page := params.Page   // Already converted to int, validated min=1
	limit := params.Limit // Already converted to int, validated min=1,max=100
	
	return h.repo.List(page, limit, params.Status)
}

type ListUsersRequest struct {
	Page   int    `query:"page" validate:"omitempty,min=1"`
	Limit  int    `query:"limit" validate:"omitempty,min=1,max=100"`
	Status string `query:"status" validate:"omitempty,oneof=active inactive"`
}
```

### Response (ctx.Resp)

Generic response builder supporting multiple content types:

```go
// JSON (most common)
ctx.Resp.WithStatus(http.StatusOK).Json(map[string]any{
	"id": 1,
	"name": "John",
})

// HTML
ctx.Resp.WithStatus(http.StatusOK).Html("<h1>Welcome</h1>")

// Plain text
ctx.Resp.WithStatus(http.StatusOK).Text("Hello, World!")

// Raw bytes with content-type
ctx.Resp.WithStatus(http.StatusOK).Raw("text/csv", csvBytes)

// Streaming (for large files, SSE, etc.)
ctx.Resp.Stream("text/event-stream", func(w http.ResponseWriter) error {
	for i := 0; i < 10; i++ {
		fmt.Fprintf(w, "data: event %d\n\n", i)
	}
	return nil
})

// Custom headers
ctx.Resp.RespHeaders = map[string][]string{
	"X-Custom": {"value1", "value2"},
}
ctx.Resp.WithStatus(http.StatusOK).Json(data)
```

---

## Layer 3: Direct Response (ctx.Resp)

Build responses with full control over status codes and content types. Use this when `ctx.Api` doesn't fit your needs.

### Common Patterns

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(ctx *request.Context, id string) error {
	user, err := h.repo.GetByID(id)
	if err != nil {
		// Return 404 with custom message
		return ctx.Resp.WithStatus(http.StatusNotFound).Json(map[string]string{
			"error": "user not found",
		})
	}
	
	// Return 200 with data
	return ctx.Resp.WithStatus(http.StatusOK).Json(user)
}

// @Route "POST /"
func (h *UserHandler) Create(ctx *request.Context, req *CreateUserRequest) error {
	user := &User{Name: req.Name}
	
	if err := h.repo.Create(user); err != nil {
		// Return 500 with error
		return ctx.Resp.WithStatus(http.StatusInternalServerError).Json(map[string]string{
			"error": "failed to create user",
		})
	}
	
	// Return 201 Created with location header
	ctx.Resp.RespHeaders = map[string][]string{
		"Location": {fmt.Sprintf("/users/%s", user.ID)},
	}
	return ctx.Resp.WithStatus(http.StatusCreated).Json(user)
}

// Streaming large file
// @Route "GET /export"
func (h *UserHandler) ExportCSV(ctx *request.Context) error {
	return ctx.Resp.Stream("text/csv", func(w http.ResponseWriter) error {
		w.Header().Set("Content-Disposition", "attachment; filename=users.csv")
		
		// Write CSV header
		fmt.Fprintln(w, "ID,Name,Email")
		
		// Stream users
		users, _ := h.repo.List()
		for _, user := range users {
			fmt.Fprintf(w, "%s,%s,%s\n", user.ID, user.Name, user.Email)
		}
		return nil
	})
}
```

---

## Layer 4: API Helper (ctx.Api) - Recommended for REST APIs

Opinionated, structured API responses wrapped in a standard format. Automatically handles status codes, error formatting, and response wrapping.

### Success Responses

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(ctx *request.Context, id string) error {
	user, err := h.repo.GetByID(id)
	if err != nil {
		return ctx.Api.NotFound("user not found")
	}
	return ctx.Api.Ok(user)  // 200 OK, wrapped in standard format
}

// @Route "POST /"
func (h *UserHandler) Create(ctx *request.Context, req *CreateUserRequest) error {
	user := &User{Name: req.Name, Email: req.Email}
	if err := h.repo.Create(user); err != nil {
		return ctx.Api.InternalError("failed to create user")
	}
	return ctx.Api.Created(user, "User created successfully")  // 201 Created
}

// List with pagination
// @Route "GET /"
func (h *UserHandler) List(params *ListRequest) error {
	users, total := h.repo.List(params.Page, params.Limit)
	return ctx.Api.OkList(users, &api_formatter.ListMeta{
		Page:       params.Page,
		Limit:      params.Limit,
		Total:      total,
		TotalPages: (total + params.Limit - 1) / params.Limit,
	})
}
```

### Error Responses

```go
// Validation errors (automatic - framework handles)
// 400 Bad Request with field-level errors
ctx.Api.ValidationError("Validation failed", []api_formatter.FieldError{
	{Field: "email", Code: "INVALID_FORMAT", Message: "invalid email"},
	{Field: "age", Code: "OUT_OF_RANGE", Message: "must be 18-100"},
})

// Client errors
ctx.Api.BadRequest("INVALID_INPUT", "email format is invalid")       // 400
ctx.Api.Unauthorized("token is expired")                             // 401
ctx.Api.Forbidden("insufficient permissions")                        // 403
ctx.Api.NotFound("user not found")                                   // 404

// Server error
ctx.Api.InternalError("database connection failed")                  // 500

// Custom status code
ctx.Api.Error(http.StatusConflict, "DUPLICATE_EMAIL", "email already exists")  // 409
```

### Response Format

All `ctx.Api.*` responses wrap data in a standard structure (configurable via `api_formatter`):

```json
{
  "success": true,
  "code": "SUCCESS",
  "message": "User retrieved",
  "data": {
    "id": "user-123",
    "name": "John Doe",
    "email": "john@example.com"
  }
}
```

Error response:
```json
{
  "success": false,
  "code": "NOT_FOUND",
  "message": "user not found",
  "data": null
}
```

Validation error:
```json
{
  "success": false,
  "code": "VALIDATION_ERROR",
  "message": "Validation failed",
  "errors": [
    {
      "field": "email",
      "code": "INVALID_FORMAT",
      "message": "invalid email format"
    }
  ]
}
```

---

## Context Utilities: Values & Transactions

### Context Values Storage

Store and retrieve request-scoped values:

```go
// Simple storage (request-scoped only, not propagated)
ctx.Set("user_id", "user-123")
ctx.Set("tenant_id", "tenant-abc")

userID := ctx.Get("user_id").(string)

// Standard context values (propagated to child contexts)
ctx.SetContextValue("tracing_id", "trace-xyz")
traceID := ctx.GetContextValue("tracing_id").(string)

// Pass to services
user, err := s.repo.FindByID(ctx, userID)  // ctx embeds context.Context
```

### Transaction Management

Manage database transactions with automatic commit/rollback:

```go
// @Route "POST /"
func (h *UserHandler) Create(ctx *request.Context, req *CreateRequest) error {
	// Begin transaction (auto-managed)
	ctx.BeginTransaction("postgres-db")
	
	// All repository calls within same handler use transaction
	user := &User{Name: req.Name}
	if err := h.repo.Create(ctx, user); err != nil {
		return err  // Auto-rollback on error
	}
	
	// Auto-commit on successful return (2xx status)
	return ctx.Api.Created(user)
}

// Multiple transactions (finalized in reverse order - LIFO)
ctx.BeginTransaction("postgres-primary")
ctx.BeginTransaction("postgres-replica")
// ... operations ...
// Auto-finalize: replica first, then primary
```

**Transaction Rules:**
- ✅ Auto-commit on 2xx status (200, 201, etc.)
- ✅ Auto-rollback on error or 4xx/5xx status
- ✅ Multiple transactions supported (LIFO finalization)
- ⚠️ Manual commit/rollback only safe in single handler (no nested calls)
- ✅ Context propagates to all repository calls

---

## Choosing the Right Layer

| Scenario | Layer | Example |
|----------|-------|---------|
| Standard REST API | Layer 4: `ctx.Api` | `ctx.Api.Ok(user)` |
| Custom status codes | Layer 3: `ctx.Resp` | `ctx.Resp.WithStatus(409).Json(data)` |
| Streaming/File download | Layer 3: `ctx.Resp` | `ctx.Resp.Stream(...)` |
| HTML/Text response | Layer 3: `ctx.Resp` | `ctx.Resp.Html(...)` |
| Raw HTTP control | Layer 1: `ctx.R`/`ctx.W` | Direct HTTP calls |
| Request data extraction | Layer 2: `ctx.Req` | `ctx.Req.QueryParam()` |
| Get client IP | Layer 2: Helper | `utils.ClientIP(ctx.R)` |
| Database context | Layer 2: `ctx.R` | `ctx.R.Context()` |

### Quick Decision Tree

```
Is it a REST API?
  ↓ Yes
  Use ctx.Api (Layer 4)
    ✓ Standard response format
    ✓ Automatic error handling
    ✓ Built-in validation error formatting
  
  ↓ No
  Do you need custom response format?
    ↓ Yes
    Use ctx.Resp (Layer 3)
      ✓ Full control over status & headers
      ✓ Multiple content types
      ✓ Streaming support
    
    ↓ No
    Use standard Go HTTP
      ✓ ctx.R for request
      ✓ ctx.W for response
```

---

## Complete Example: Multi-Layer Usage

```go
// @Route "POST /{tenantId}/users"
func (h *UserHandler) CreateUser(
	ctx *request.Context,
	tenantId string,
	req *CreateUserRequest,
) error {
	// Layer 2: Extract client IP using helper
	import "github.com/primadi/lokstra/common/utils"
	clientIP := utils.ClientIP(ctx.R)
	
	// Layer 2: Extract other headers
	userAgent := ctx.Req.HeaderParam("User-Agent", "unknown")
	apiKey := ctx.Req.HeaderParam("X-API-Key", "")
	
	// Validate API key
	if apiKey == "" {
		return ctx.Api.Unauthorized("missing API key")  // Layer 4
	}
	
	// Layer 1: Use standard context for database timeout
	dbCtx, cancel := context.WithTimeout(ctx.R.Context(), 5*time.Second)
	defer cancel()
	
	// Create transaction
	ctx.BeginTransaction("postgres-db")
	
	// Create user with metadata
	user := &User{
		TenantID:  tenantId,
		Name:      req.Name,
		Email:     req.Email,
		CreatedBy: apiKey,
		ClientIP:  clientIP,
	}
	
	if err := h.repo.Create(ctx, user); err != nil {
		// If business logic error, use custom layer 3
		if errors.Is(err, ErrEmailExists) {
			return ctx.Resp.WithStatus(http.StatusConflict).Json(map[string]string{
				"error": "email already exists",
				"code":  "DUPLICATE_EMAIL",
			})
		}
		// Generic error - use layer 4
		return ctx.Api.InternalError("failed to create user")
	}
	
	// Success - layer 4 opinionated response
	return ctx.Api.Created(user, "User created successfully")
	// Transaction auto-commits because status is 201 (2xx)
}

// Service layer example
func (s *AuthService) Register(
	ctx *request.Context,  // Can be used as context.Context
	tenantID string,
	params *RegisterRequest,
) (*RegisterResponse, error) {
	// Pass ctx directly to repository (it embeds context.Context)
	existingUser, err := s.repo.FindByUsername(ctx, params.Username)
	if err != nil {
		return nil, err
	}
	if existingUser != nil {
		return nil, ErrUsernameTaken
	}
	
	// ... more service logic ...
	
	return &RegisterResponse{...}, nil
}
```

### Service Implementation Best Practices

When implementing `@Service` annotated services that receive `request.Context`:

```go
// @Service "user-service"
type UserService struct {
	// @Inject "user-repository"
	repo UserRepository
}

// Service method receiving request.Context from handler
func (s *UserService) Create(ctx *request.Context, req *CreateUserRequest) (*User, error) {
	// Pass ctx directly - it embeds context.Context
	// Repository expects context.Context
	user := &User{Name: req.Name, Email: req.Email}
	
	if err := s.repo.Create(ctx, user); err != nil {
		return nil, err
	}
	
	return user, nil
}

// Repository interface (uses standard context.Context)
type UserRepository interface {
	Create(ctx context.Context, user *User) error
	FindByID(ctx context.Context, id string) (*User, error)
	FindByEmail(ctx context.Context, email string) (*User, error)
}

// Repository implementation
type PostgresUserRepository struct {
	db *sql.DB
}

func (r *PostgresUserRepository) Create(ctx context.Context, user *User) error {
	// ctx can be passed directly to database queries
	// Works with both context.Context and request.Context (embedding)
	return r.db.ExecContext(ctx, "INSERT INTO users ...", user.Name)
}
```

**Key Points:**
- ✅ `request.Context` embeds `context.Context` - passes anywhere context is expected
- ✅ Repositories should use `context.Context`, not `request.Context`
- ✅ Services can accept either `request.Context` or `context.Context`
- ✅ Pass `ctx` directly to repositories - no need for `ctx.R.Context()`
- ✅ Transaction context automatically propagated through embedded context

---

## Error Handling Patterns

### 1. Automatic Error Response (Return error)

Framework automatically converts errors to 500 Internal Server Error:

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) {
	if id == "" {
		// Returns 500 with error message
		return nil, fmt.Errorf("id required")
	}
	
	user, err := h.UserRepo.GetByID(id)
	if err != nil {
		// Returns 500 with error message
		return nil, fmt.Errorf("failed to get user: %w", err)
	}
	
	return user, nil
}
```

**Use Case:** Simple error handling, when 500 is acceptable for all errors

### 2. Custom Error Responses (With Context)

Use `request.Context` to return specific HTTP status codes:

```go
// @Route "GET /{id}"
func (h *UserHandler) GetByID(ctx *request.Context, id string) error {
	// Validation error - 400
	if id == "" {
		return ctx.Api.BadRequest("id is required")
	}
	
	// Resource not found - 404
	user, err := h.UserRepo.GetByID(id)
	if err == sql.ErrNoRows {
		return ctx.Api.NotFound(fmt.Sprintf("user %s not found", id))
	}
	if err != nil {
		// Database error - 500
		return ctx.Api.InternalServerError("database error")
	}
	
	// Success - 200
	return ctx.Api.Ok(user)
}
```

### 3. Domain-Specific Errors

Create custom error types for better error handling:

```go
// domain/errors.go
type UserError struct {
	Code    string
	Message string
	Status  int
}

func (e *UserError) Error() string {
	return e.Message
}

var (
	ErrUserNotFound     = &UserError{"USER_NOT_FOUND", "user not found", 404}
	ErrInvalidEmail     = &UserError{"INVALID_EMAIL", "invalid email format", 400}
	ErrDuplicateEmail   = &UserError{"DUPLICATE_EMAIL", "email already exists", 409}
	ErrUnauthorized     = &UserError{"UNAUTHORIZED", "unauthorized access", 401}
)

// Handler
// @Route "POST /"
func (h *UserHandler) Create(ctx *request.Context, req *domain.CreateUserRequest) error {
	// Check for duplicate
	existing, _ := h.UserRepo.GetByEmail(req.Email)
	if existing != nil {
		return ctx.Api.Conflict("email already exists")
	}
	
	user := &domain.User{
		Name:  req.Name,
		Email: req.Email,
	}
	
	created, err := h.UserRepo.Create(user)
	if err != nil {
		return ctx.Api.InternalServerError("failed to create user")
	}
	
	return ctx.Api.Created(created)
}
```

### 4. Validation Errors

Validation errors are automatically handled by framework:

```go
type CreateUserRequest struct {
	Name     string `json:"name" validate:"required,min=3,max=50"`
	Email    string `json:"email" validate:"required,email"`
	Age      int    `json:"age" validate:"omitempty,min=18,max=150"`
	Password string `json:"password" validate:"required,min=8,containsany=!@#$%"`
}

// @Route "POST /"
func (h *UserHandler) Create(req *CreateUserRequest) (*domain.User, error) {
	// If validation fails, framework returns 400 Bad Request with validation errors
	// This code only runs if validation passes
	
	user := &domain.User{
		Name:  req.Name,
		Email: req.Email,
	}
	return h.UserRepo.Create(user)
}
```

**Validation Error Response Format:**

```json
{
  "error": "validation failed",
  "details": [
    {
      "field": "email",
      "message": "invalid email format"
    },
    {
      "field": "password",
      "message": "must be at least 8 characters"
    }
  ]
}
```

---

## Middleware Configuration

### Global Handler Middlewares

Applied to all routes in the handler:

```go
// @Handler name="user-handler", prefix="/api/users", middlewares=["auth", "logger"]
type UserHandler struct {
	// @Inject "user-repository"
	UserRepo domain.UserRepository
}

// All routes below inherit ["auth", "logger"] middlewares
// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) { }

// @Route "POST /"
func (h *UserHandler) Create(req *domain.CreateUserRequest) (*domain.User, error) { }
```

### Route-Specific Middlewares

Add additional middlewares to specific routes:

```go
// @Handler name="user-handler", prefix="/api/users", middlewares=["logger"]
type UserHandler struct {
	// @Inject "user-repository"
	UserRepo domain.UserRepository
}

// Only logger middleware
// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) { }

// logger + auth middlewares
// @Route "POST /", middlewares=["auth"]
func (h *UserHandler) Create(req *domain.CreateUserRequest) (*domain.User, error) { }

// logger + auth + admin middlewares
// @Route "DELETE /{id}", middlewares=["auth", "admin"]
func (h *UserHandler) Delete(id string) error { }
```

### Middleware Registration in main.go

Middlewares must be registered before bootstrap:

```go
package main

import (
	"github.com/primadi/lokstra/lokstra_init"
	"github.com/primadi/lokstra/middleware/recovery"
	"github.com/primadi/lokstra/middleware/request_logger"
	"myapp/middleware/auth"
	"myapp/middleware/admin"
	
	_ "myapp/modules/user/application" // Import handlers
)

func main() {
	// Register middlewares
	middlewares := map[string]func(http.Handler) http.Handler{
		"recovery":       recovery.Middleware(nil),
		"logger":         request_logger.Middleware(nil),
		"auth":           auth.Middleware(),
		"admin":          admin.AdminMiddleware(),
		"rate-limit":     ratelimit.Middleware(100, time.Minute),
	}
	
	// Bootstrap with middlewares
	lokstra_init.BootstrapAndRun(
		lokstra_init.WithConfigFiles("configs/config.yaml"),
		lokstra_init.WithMiddlewares(middlewares),
	)
}
```

### Custom Middleware Example

```go
// middleware/auth/auth.go
package auth

import (
	"net/http"
	"strings"
	"github.com/primadi/lokstra/core/request"
)

func Middleware() func(http.Handler) http.Handler {
	return func(next http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Get token from header
			token := r.Header.Get("Authorization")
			if token == "" {
				ctx := request.NewContext(w, r)
				ctx.Api.Unauthorized("missing authorization token")
				return
			}
			
			// Validate token
			token = strings.TrimPrefix(token, "Bearer ")
			if !validateToken(token) {
				ctx := request.NewContext(w, r)
				ctx.Api.Unauthorized("invalid token")
				return
			}
			
			// Add user to context
			userID := extractUserID(token)
			r = r.WithContext(context.WithValue(r.Context(), "user_id", userID))
			
			next.ServeHTTP(w, r)
		})
	}
}
```

**Middleware Order:**
1. Handler global middlewares
2. Route-specific middlewares
3. Handler function

Example: `["logger"]` (handler) + `["auth", "admin"]` (route) = `["logger", "auth", "admin"]`

---

## Project Structure & Organization

### Recommended Structure (DDD Bounded Context)

```
modules/
├── user/
│   ├── domain/                      # Business logic & interfaces
│   │   ├── user.go                  # Domain model
│   │   ├── user_dto.go              # Request/Response DTOs
│   │   ├── user_repository.go       # Repository interface
│   │   └── user_service.go          # Service interface (optional)
│   ├── application/                 # Handlers (@Handler)
│   │   ├── user_handler.go          # Main CRUD handler
│   │   ├── user_admin_handler.go    # Admin operations (optional)
│   │   └── zz_generated.lokstra.go  # Auto-generated
│   └── infrastructure/              # Data access (@Service)
│       ├── postgres_user_repo.go    # PostgreSQL implementation
│       └── zz_generated.lokstra.go  # Auto-generated
├── order/
│   ├── domain/
│   │   ├── order.go
│   │   ├── order_dto.go
│   │   └── order_repository.go
│   ├── application/
│   │   ├── order_handler.go
│   │   └── zz_generated.lokstra.go
│   └── infrastructure/
│       ├── postgres_order_repo.go
│       └── zz_generated.lokstra.go
└── ...
```

### Handler File Organization Options

#### Option 1: Single Handler Per Module (Recommended)

Best for small to medium modules with 5-15 endpoints:

```
modules/user/application/
├── user_handler.go           # All user CRUD operations
└── zz_generated.lokstra.go   # Auto-generated
```

**user_handler.go:**

```go
// @Handler name="user-handler", prefix="/api/users"
type UserHandler struct {
	// @Inject "user-repository"
	UserRepo domain.UserRepository
}

// @Route "GET /"
func (h *UserHandler) List() ([]*domain.User, error) { }

// @Route "GET /{id}"
func (h *UserHandler) GetByID(id string) (*domain.User, error) { }

// @Route "POST /"
func (h *UserHandler) Create(req *domain.CreateUserRequest) (*domain.User, error) { }

// @Route "PUT /{id}"
func (h *UserHandler) Update(id string, req *domain.UpdateUserRequest) (*domain.User, error) { }

// @Route "DELETE /{id}"
func (h *UserHandler) Delete(id string) error { }
```

#### Option 2: Multiple Handlers (Public/Admin Separation)

Best for modules with different access levels:

```
modules/user/application/
├── user_public_handler.go    # Public endpoints
├── user_admin_handler.go     # Admin endpoints
└── zz_generated.lokstra.go
```

**user_public_handler.go:**

```go
// @Handler name="user-public-handler", prefix="/api/users"
type UserPublicHandler struct {
	// @Inject "user-repository"
	UserRepo domain.UserRepository
}

// @Route "GET /"
func (h *UserPublicHandler) List() ([]*domain.User, error) { }

// @Route "GET /{id}"
func (h *UserPublicHandler) GetByID(id string) (*domain.User, error) { }
```

**user_admin_handler.go:**

```go
// @Handler name="user-admin-handler", prefix="/api/admin/users", middlewares=["auth", "admin"]
type UserAdminHandler struct {
	// @Inject "user-repository"
	UserRepo domain.UserRepository
}

// @Route "POST /"
func (h *UserAdminHandler) Create(req *domain.CreateUserRequest) (*domain.User, error) { }

// @Route "PUT /{id}"
func (h *UserAdminHandler) Update(id string, req *domain.UpdateUserRequest) (*domain.User, error) { }

// @Route "DELETE /{id}"
func (h *UserAdminHandler) Delete(id string) error { }

// @Route "POST /{id}/suspend"
func (h *UserAdminHandler) Suspend(id string) error { }
```

#### Option 3: Feature-Based Handlers

Best for large modules with distinct feature sets:

```
modules/user/application/
├── user_crud_handler.go          # Basic CRUD
├── user_auth_handler.go          # Login/logout/password
├── user_profile_handler.go       # Profile management
└── zz_generated.lokstra.go
```

### Naming Conventions

**Handler Nam

…(truncated)
