Middleware
Built-in middleware collection for request processing
Overview
Lokstra provides a comprehensive collection of built-in middleware for common HTTP concerns including security, logging, compression, and error handling. All middleware can be configured programmatically or via YAML configuration.
Available Middleware
| Middleware | Purpose | Priority |
|---|---|---|
| Recovery | Panic recovery | First |
| Request Logger | Request logging | Early |
| Slow Request Logger | Slow request detection | Early |
| CORS | Cross-origin handling | Early |
| Body Limit | Request size protection | Before parsing |
| Gzip Compression | Response compression | Late |
Note: JWT Auth and Access Control middleware have been moved to github.com/primadi/lokstra-auth
Advanced Topics
- Inline Parameters - Pass parameters directly in middleware name (e.g.,
"rate-limit max=100") - Custom Middleware - Create your own middleware (Coming Soon)
Quick Start
Programmatic Configuration
import (
"github.com/primadi/lokstra/middleware/recovery"
"github.com/primadi/lokstra/middleware/request_logger"
"github.com/primadi/lokstra/middleware/cors"
"github.com/primadi/lokstra/middleware/body_limit"
"github.com/primadi/lokstra/middleware/gzipcompression"
)
// Apply middleware to router
router.Use(
recovery.Middleware(&recovery.Config{
EnableStackTrace: false,
}),
request_logger.Middleware(&request_logger.Config{
EnableColors: true,
}),
cors.Middleware([]string{"*"}),
body_limit.Middleware(&body_limit.Config{
MaxSize: 10 * 1024 * 1024, // 10MB
}),
gzipcompression.Middleware(&gzipcompression.Config{
MinSize: 1024, // 1KB
}),
)
YAML Configuration
middlewares:
- type: recovery
params:
enable_stack_trace: false
enable_logging: true
- type: request_logger
params:
enable_colors: true
skip_paths: ["/health", "/metrics"]
- type: slow_request_logger
params:
threshold: 500 # milliseconds
enable_colors: true
- type: cors
params:
allow_origins: ["*"]
- type: body_limit
params:
max_size: 10485760 # 10MB
skip_on_path: ["/upload/**"]
- type: gzip_compression
params:
min_size: 1024
compression_level: -1
Note: For JWT Auth and Access Control YAML configuration, see github.com/primadi/lokstra-auth
Middleware Order
The order of middleware is critical for proper functionality:
// Recommended order:
router.Use(
// 1. Recovery - catch all panics from other middleware
recovery.Middleware(&recovery.Config{}),
// 2. Request Logger - log all requests
request_logger.Middleware(&request_logger.Config{}),
// 3. Slow Request Logger - detect performance issues
slow_request_logger.Middleware(&slow_request_logger.Config{
Threshold: 500 * time.Millisecond,
}),
// 4. CORS - handle preflight requests early
cors.Middleware([]string{"*"}),
// 5. Body Limit - protect memory before parsing
body_limit.Middleware(&body_limit.Config{
MaxSize: 10 * 1024 * 1024,
}),
// 6. Gzip Compression - compress responses (last)
gzipcompression.Middleware(&gzipcompression.Config{
MinSize: 1024,
}),
)
Note: For authentication middleware (JWT, Access Control), see github.com/primadi/lokstra-auth
Why this order?
- Recovery first - Catches panics from all other middleware
- Logging early - Records all requests, even failed ones
- CORS early - Handles preflight before authentication
- Body limit before parsing - Prevents memory exhaustion
- Compression last - Compresses final response
Registry Integration
Registering Middleware
import (
"github.com/primadi/lokstra/middleware/recovery"
"github.com/primadi/lokstra/middleware/cors"
// ... other middleware
)
func init() {
// Register all middleware
recovery.Register()
cors.Register()
body_limit.Register()
gzipcompression.Register()
request_logger.Register()
slow_request_logger.Register()
}
Using from Registry
// Get middleware by name
recoveryMw := lokstra_registry.GetMiddleware("recovery", map[string]any{
"enable_stack_trace": false,
})
corsMw := lokstra_registry.GetMiddleware("cors", map[string]any{
"allow_origins": []string{"*"},
})
// Apply to router
router.Use(recoveryMw, corsMw)
Common Patterns
Production Setup
// Production-optimized configuration
router.Use(
recovery.Middleware(&recovery.Config{
EnableStackTrace: false, // Hide stack traces
EnableLogging: true,
}),
request_logger.Middleware(&request_logger.Config{
EnableColors: false, // No colors in logs
SkipPaths: []string{"/health", "/metrics"},
}),
slow_request_logger.Middleware(&slow_request_logger.Config{
Threshold: 1 * time.Second,
EnableColors: false,
}),
cors.Middleware([]string{
"https://app.example.com",
"https://admin.example.com",
}),
body_limit.Middleware(&body_limit.Config{
MaxSize: 5 * 1024 * 1024, // 5MB max
}),
gzipcompression.Middleware(&gzipcompression.Config{
MinSize: 1024,
CompressionLevel: gzip.BestSpeed,
}),
)
Development Setup
// Development-friendly configuration
router.Use(
recovery.Middleware(&recovery.Config{
EnableStackTrace: true, // Show stack traces for debugging
EnableLogging: true,
}),
request_logger.Middleware(&request_logger.Config{
EnableColors: true, // Colored terminal output
SkipPaths: []string{},
}),
slow_request_logger.Middleware(&slow_request_logger.Config{
Threshold: 200 * time.Millisecond, // Lower threshold
EnableColors: true,
}),
cors.Middleware([]string{"*"}), // Allow all origins
)
Note: For selective middleware patterns with authentication, see github.com/primadi/lokstra-auth
Custom Error Messages
router.Use(
body_limit.Middleware(&body_limit.Config{
MaxSize: 10 * 1024 * 1024,
Message: "File too large. Maximum size is 10MB",
StatusCode: http.StatusRequestEntityTooLarge,
}),
)
Path-Based Configuration
router.Use(
body_limit.Middleware(&body_limit.Config{
MaxSize: 1 * 1024 * 1024, // 1MB default
SkipOnPath: []string{
"/upload/**", // Skip limit for uploads
"/import/**", // Skip for imports
},
}),
request_logger.Middleware(&request_logger.Config{
SkipPaths: []string{
"/health",
"/metrics",
"/.well-known/**",
},
}),
)
Performance Considerations
Middleware Overhead
| Middleware | Overhead | Notes |
|---|---|---|
| Recovery | ~50ns | Minimal, deferred only |
| Request Logger | ~1-5μs | Time recording + formatting |
| CORS | ~500ns | Header checks |
| Body Limit | ~100ns | Wrapper allocation |
| Gzip | ~50-500μs | Depends on response size |
Optimization Tips
1. Skip unnecessary paths:
request_logger.Middleware(&request_logger.Config{
SkipPaths: []string{"/health", "/metrics"}, // Skip frequent health checks
})
2. Use appropriate compression:
gzipcompression.Middleware(&gzipcompression.Config{
MinSize: 1024, // Don't compress small responses
ExcludedContentTypes: []string{
"image/jpeg",
"video/mp4", // Already compressed
},
})
3. Set realistic body limits:
body_limit.Middleware(&body_limit.Config{
MaxSize: 5 * 1024 * 1024, // Lower limit = better protection
})
4. Enable gzip only when beneficial:
// Don't compress API responses < 1KB
gzipcompression.Middleware(&gzipcompression.Config{
MinSize: 1024,
})
Testing
Testing with Middleware
func TestHandlerWithMiddleware(t *testing.T) {
// Create router with middleware
router := lokstra.NewRouter()
router.Use(
recovery.Middleware(&recovery.Config{}),
request_logger.Middleware(&request_logger.Config{}),
)
// Add test handler
router.GET("/test", func(c *request.Context) error {
return c.Api.Ok("success")
})
// Test request
req := httptest.NewRequest("GET", "/test", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
assert.Equal(t, 200, rec.Code)
}
Creating Custom Middleware
Basic Pattern
package mymiddleware
import "github.com/primadi/lokstra/core/request"
type Config struct {
Option1 string
Option2 int
}
func Middleware(cfg *Config) request.HandlerFunc {
return request.HandlerFunc(func(c *request.Context) error {
// Pre-processing
// Call next handler
err := c.Next()
// Post-processing
return err
})
}
With Factory Function
func MiddlewareFactory(params map[string]any) request.HandlerFunc {
cfg := &Config{
Option1: utils.GetValueFromMap(params, "option1", "default"),
Option2: utils.GetValueFromMap(params, "option2", 10),
}
return Middleware(cfg)
}
func Register() {
lokstra_registry.RegisterMiddlewareFactory("mymiddleware", MiddlewareFactory,
lokstra_registry.AllowOverride(true))
}
Best Practices
1. Always Use Recovery First
// ✅ Good
router.Use(
recovery.Middleware(&recovery.Config{}), // First
// ... other middleware
)
// 🚫 Bad
router.Use(
cors.Middleware([]string{"*"}),
recovery.Middleware(&recovery.Config{}), // Too late
)
2. Skip Health Checks from Logging
// ✅ Good
request_logger.Middleware(&request_logger.Config{
SkipPaths: []string{"/health", "/metrics"},
})
// 🚫 Bad - logs every health check
request_logger.Middleware(&request_logger.Config{})
3. Use Appropriate Body Limits
// ✅ Good - different limits for different endpoints
body_limit.Middleware(&body_limit.Config{
MaxSize: 1 * 1024 * 1024, // 1MB default
SkipOnPath: []string{"/upload/**"}, // Higher limit elsewhere
})
// 🚫 Bad - one size for all
body_limit.Middleware(&body_limit.Config{
MaxSize: 100 * 1024 * 1024, // 100MB everywhere
})
4. Disable Stack Traces in Production
// ✅ Good
recovery.Middleware(&recovery.Config{
EnableStackTrace: os.Getenv("ENV") == "development",
})
// 🚫 Bad - exposes internal details
recovery.Middleware(&recovery.Config{
EnableStackTrace: true,
})
5. Configure CORS Properly
// ✅ Good - specific origins in production
allowedOrigins := []string{"*"}
if os.Getenv("ENV") == "production" {
allowedOrigins = []string{
"https://app.example.com",
"https://admin.example.com",
}
}
cors.Middleware(allowedOrigins)
// 🚫 Bad - wildcard in production
cors.Middleware([]string{"*"})
See Also
- Router - Router configuration
- Request - Request handling
- Response - Response formatting
- Registry - Middleware registration
Related Guides
- Security Best Practices - Security patterns
- Performance Optimization - Performance tips
- Testing Middleware - Testing strategies