Middleware - Essential Guide
Request/response processing made easy
Time: 25-30 minutes β’ Level: Beginner
π What Youβll Learn
- β What middleware is and why you need it
- β Using middleware in 2 ways (direct & by name)
- β Global vs per-route middleware
- β Middleware registration and factories
- β Built-in middleware (logging, CORS, auth)
π― What is Middleware?
Middleware is a function that runs before (or after) your handler. Itβs perfect for:
- π Logging - Log every request
- π Authentication - Verify user identity
- π‘οΈ Authorization - Check permissions
- π CORS - Handle cross-origin requests
- β±οΈ Rate Limiting - Prevent abuse
- π Metrics - Collect statistics
Request Flow with Middleware:
HTTP Request
β
Middleware 1 (logging)
β
Middleware 2 (auth)
β
Your Handler
β
Response
π Quick Start (2 Minutes)
package main
import (
"github.com/primadi/lokstra"
"github.com/primadi/lokstra/middleware/request_logger"
)
func main() {
r := lokstra.NewRouter("api")
// Add logging middleware
r.Use(request_logger.Middleware(nil))
r.GET("/users", func() []string {
return []string{"Alice", "Bob"}
})
app := lokstra.NewApp("demo", ":3000", r)
if err := app.Run(30 * time.Second); err != nil {
fmt.Println("Error starting server:", err)
}
}
Test it:
curl http://localhost:3000/users
# Console will show: [LOG] GET /users 200 OK (2ms)
π Two Ways to Use Middleware
Lokstra supports 2 methods for using middleware:
Method 1: Direct Function Call
Use when: Simple apps, code-only configuration
import (
"github.com/primadi/lokstra/middleware/request_logger"
"github.com/primadi/lokstra/middleware/cors"
)
r := lokstra.NewRouter("api")
// Use middleware directly
r.Use(
request_logger.Middleware(nil),
cors.Middleware(corsConfig),
)
Pros:
- β Simple and direct
- β Type-safe
- β No registration needed
Cons:
- β Hardcoded in code
- β Canβt configure via YAML
Method 2: By Name (Registry Pattern)
Use when: Config-driven apps, reusable middleware
import (
"github.com/primadi/lokstra/lokstra_registry"
"github.com/primadi/lokstra/middleware/request_logger"
)
// Step 1: Register middleware factory (once, usually in main.go)
lokstra_registry.RegisterMiddlewareFactory("logger",
request_logger.MiddlewareFactory)
// Step 2: Register named instances with config
lokstra_registry.RegisterMiddlewareName("logger_std", "logger", map[string]any{
"show_body": false,
"show_headers": true,
})
lokstra_registry.RegisterMiddlewareName("logger_verbose", "logger", map[string]any{
"show_body": true,
"show_headers": true,
})
// Step 3: Use by name
r.Use("logger_std") // Uses standard config
// Different router with verbose logging
adminRouter := lokstra.NewRouter("admin")
adminRouter.Use("logger_verbose") // Uses verbose config
Pros:
- β Reusable with different configs
- β Can be configured via YAML
- β Environment-specific configs
Cons:
- β Requires registration setup
- β String-based (not type-safe)
When to Use Which?
| Scenario | Method | Reason |
|---|---|---|
| Simple app, few middleware | Direct | Less code, simpler |
| Multiple environments | By Name | Different configs per env |
| YAML-driven config | By Name | Can configure externally |
| Prototype/learning | Direct | Faster to write |
| Production app | By Name | More flexible |
π§ Middleware Scopes
1. Global Middleware
Applied to all routes in the router:
r := lokstra.NewRouter("api")
// These run for EVERY route
r.Use(loggingMiddleware, corsMiddleware)
r.GET("/users", getUsers) // Has logging + CORS
r.POST("/products", addProduct) // Has logging + CORS
2. Per-Route Middleware
Applied to specific routes only:
r.GET("/public", publicHandler) // No auth
// Method 1: Direct
r.GET("/private", privateHandler, authMiddleware)
// Method 2: By name
r.GET("/admin", adminHandler, "auth_jwt", "admin_check")
3. Group Middleware
Applied to all routes in a group:
// API v1 - basic auth
v1 := r.AddGroup("/v1")
v1.Use(basicAuthMiddleware)
v1.GET("/users", getUsersV1)
// API v2 - JWT auth
v2 := r.AddGroup("/v2")
v2.Use(jwtAuthMiddleware)
v2.GET("/users", getUsersV2)
4. Mixed Middleware
Combine global, group, and route-level:
// Global: All routes
r.Use(loggingMiddleware)
// Group: /admin/* routes
admin := r.AddGroup("/admin")
admin.Use(authMiddleware)
// Route-specific: Only this route
admin.GET("/danger", dangerHandler, confirmMiddleware)
// Final middleware chain for /admin/danger:
// 1. loggingMiddleware (global)
// 2. authMiddleware (group)
// 3. confirmMiddleware (route)
// 4. dangerHandler
π Middleware Factory Pattern
Understanding the Pattern
Factory = Function that creates middleware with config
// Middleware Factory Type
type MiddlewareFactory = func(config map[string]any) request.HandlerFunc
// Example: Logger Factory
func LoggerFactory(config map[string]any) request.HandlerFunc {
showBody := config["show_body"].(bool)
showHeaders := config["show_headers"].(bool)
// Return the actual middleware
return func(ctx *request.Context) error {
if showBody {
// Log body
}
if showHeaders {
// Log headers
}
return ctx.Next()
}
}
Step-by-Step Usage
// 1. Register the factory (once)
lokstra_registry.RegisterMiddlewareFactory("logger", LoggerFactory)
// 2. Create named instances (different configs)
lokstra_registry.RegisterMiddlewareName("logger_dev", "logger", map[string]any{
"show_body": true,
"show_headers": true,
})
lokstra_registry.RegisterMiddlewareName("logger_prod", "logger", map[string]any{
"show_body": false,
"show_headers": false,
})
// 3. Use by name
devRouter := lokstra.NewRouter("dev-api")
devRouter.Use("logger_dev") // Verbose logging
prodRouter := lokstra.NewRouter("prod-api")
prodRouter.Use("logger_prod") // Minimal logging
π¨ Built-in Middleware
Lokstra provides several ready-to-use middleware:
1. Request Logger
import "github.com/primadi/lokstra/middleware/request_logger"
// Direct usage
r.Use(request_logger.Middleware(nil))
// Or with config
r.Use(request_logger.Middleware(map[string]any{
"show_body": true,
}))
// Or by name
lokstra_registry.RegisterMiddlewareFactory("logger",
request_logger.MiddlewareFactory)
lokstra_registry.RegisterMiddlewareName("logger_std", "logger", nil)
r.Use("logger_std")
Output:
[INFO] GET /users 200 OK (5ms)
[INFO] POST /users 201 Created (15ms)
2. CORS
import "github.com/primadi/lokstra/middleware/cors"
corsConfig := map[string]any{
"allow_origins": []string{"*"},
"allow_methods": []string{"GET", "POST", "PUT", "DELETE"},
"allow_headers": []string{"Content-Type", "Authorization"},
}
// Direct
r.Use(cors.Middleware(corsConfig))
// By name
lokstra_registry.RegisterMiddlewareFactory("cors", cors.MiddlewareFactory)
lokstra_registry.RegisterMiddlewareName("cors_all", "cors", corsConfig)
r.Use("cors_all")
3. JWT Authentication
import "github.com/primadi/lokstra/middleware/jwtauth"
jwtConfig := map[string]any{
"secret_key": "your-secret-key",
}
// Per-route
r.GET("/public", publicHandler)
r.GET("/private", privateHandler, jwtauth.MiddlewareFactory(jwtConfig))
// Or by name
lokstra_registry.RegisterMiddlewareFactory("jwt", jwtauth.MiddlewareFactory)
lokstra_registry.RegisterMiddlewareName("jwt_std", "jwt", jwtConfig)
r.GET("/private", privateHandler, "jwt_std")
π§ͺ Examples
All examples are runnable!
01 - Logging Middleware
Learn: Request logging setup
Time: 5 minutes
r.Use(request_logger.Middleware(nil))
02 - Authentication
Learn: Custom authentication and authorization with Group API
Time: 10 minutes
apiGroup := r.AddGroup("/api")
apiGroup.Use(authMiddleware)
adminGroup := r.AddGroup("/api/admin")
adminGroup.Use(authMiddleware, adminMiddleware)
03 - CORS Configuration
Learn: Cross-origin setup for APIs
Time: 7 minutes
r.Use(cors.Middleware(allowedOrigins))
π― Common Patterns
Pattern 1: Standard API Setup
r := lokstra.NewRouter("api")
// Every API needs these
r.Use("logger", "cors")
// Public routes
r.GET("/health", healthCheck)
// Protected routes
auth := r.AddGroup("/api")
auth.Use("jwt_auth")
auth.GET("/users", getUsers)
Pattern 2: Multi-Environment Config
// Development - verbose
if env == "dev" {
r.Use("logger_verbose", "cors_dev")
}
// Production - minimal
if env == "prod" {
r.Use("logger_minimal", "cors_prod")
}
Pattern 3: Progressive Authentication
// Public
r.GET("/products", listProducts)
// Basic auth
registered := r.AddGroup("/registered")
registered.Use("basic_auth")
registered.GET("/profile", getProfile)
// Admin
admin := r.AddGroup("/admin")
admin.Use("jwt_auth", "admin_check")
admin.DELETE("/users", deleteUser)
π« Common Mistakes
β Donβt: Add same middleware twice
r.Use(loggingMiddleware)
r.Use(loggingMiddleware) // β Duplicate!
β Do: Add once at appropriate level
r.Use(loggingMiddleware) // Global - runs once
β Donβt: Forget to call Next()
func badMiddleware(ctx *request.Context) error {
// Do something
return nil // β Didn't call ctx.Next()!
}
β Do: Always call Next() unless stopping
func goodMiddleware(ctx *request.Context) error {
// Do something before handler
err := ctx.Next() // β
Continue chain
// Do something after handler
return err
}
β Donβt: Mix named and unnamed inconsistently
r.Use("logger")
r.Use(corsMiddleware) // β Mixing styles in same router
β Do: Pick one style per router
// All by name
r.Use("logger", "cors")
// Or all direct
r.Use(loggingMiddleware, corsMiddleware)
π Best Practices
1. Register Factories Early
// β
Good - in main() or init()
func main() {
registerMiddleware() // All registrations
setupRouters() // Use middleware
startServer()
}
2. Use Descriptive Names
// β
Good
lokstra_registry.RegisterMiddlewareName("logger_verbose", "logger", config)
lokstra_registry.RegisterMiddlewareName("auth_jwt_admin", "jwt", config)
// π« Bad
lokstra_registry.RegisterMiddlewareName("mw1", "logger", config)
lokstra_registry.RegisterMiddlewareName("a", "jwt", config)
3. Order Matters
// β
Good - logical order
r.Use(
"logger", // 1. Log first
"cors", // 2. Handle CORS
"rate_limit", // 3. Rate limit
"auth", // 4. Authenticate
)
// π« Bad - auth before rate limit?
r.Use("auth", "rate_limit") // Attacker can spam auth!
π Whatβs Next?
You now understand:
- β What middleware is and why itβs useful
- β Two ways to use middleware (direct & by name)
- β Global, per-route, and group middleware
- β Middleware factory pattern
- β Built-in middleware (logging, CORS, auth)
π‘ Practice with Examples:
π examples/ - Three working examples:
- 01-logging-middleware - Auto-log all requests
- 02-auth-middleware - Custom auth & authorization
- 03-cors-middleware - CORS configuration
Next Steps:
Immediate: π 04 - Configuration - Learn config patterns
Related:
π Quick Reference
Direct Usage
r.Use(middleware1, middleware2)
r.GET("/path", handler, middleware3)
By Name Usage
// Register
lokstra_registry.RegisterMiddlewareFactory(type, factory)
lokstra_registry.RegisterMiddlewareName(name, type, config)
// Use
r.Use("name1", "name2")
r.GET("/path", handler, "name3")
Built-in Middleware
request_logger.Middleware(config)- Request loggingcors.Middleware(config)- CORS handlingjwtauth.MiddlewareFactory(config)- JWT authaccesscontrol.MiddlewareFactory(config)- Access control
Continue learning β 04 - Configuration