Middleware Guide
Learn how to use middleware for cross-cutting concerns like authentication, logging, CORS, and more in your Squirrel applications.
AuthenticationLoggingCORS
What is Middleware?
Understanding the middleware pattern in web applications
Middleware is a function that sits between the incoming request and the outgoing response. It can modify the request, response, or both, and decide whether to pass control to the next middleware in the chain.
Middleware Flow:
Request → Middleware 1 → Middleware 2 → Handler → Response
Each middleware can modify the request/response or stop the chain
Creating Custom Middleware
Build your own middleware functions
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/squirrel-land/squirrel"
)
// Logging middleware
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Call the next handler
next.ServeHTTP(w, r)
// Log after the request is processed
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// Authentication middleware
func authMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Validate token (simplified)
if token != "Bearer valid-token" {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
// Token is valid, continue to next handler
next.ServeHTTP(w, r)
})
}
func main() {
mux := squirrel.NewSqurlMux()
// Apply middleware globally
mux.Use(loggingMiddleware)
// Public routes
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/login", loginHandler)
// Protected routes
protected := mux.Group("/api")
protected.Use(authMiddleware)
protected.Get("/profile", profileHandler)
protected.Get("/data", dataHandler)
http.ListenAndServe(":8080", mux)
}Built-in Middleware
Use Squirrels built-in middleware for common tasks
package main
import (
"net/http"
"github.com/squirrel-land/squirrel"
"github.com/squirrel-land/squirrel/middleware"
)
func main() {
mux := squirrel.NewSqurlMux()
// CORS middleware with configuration
corsConfig := middleware.CORSConfig{
AllowOrigins: []string{"https://example.com", "https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
MaxAge: 3600,
}
mux.Use(middleware.CORS(corsConfig))
// Your routes
mux.Get("/api/users", getUsersHandler)
mux.Post("/api/users", createUserHandler)
http.ListenAndServe(":8080", mux)
}Middleware Chain Order
Understanding the order of middleware execution
Important: Middleware order matters! Middleware is executed in the order its added.
func main() {
mux := squirrel.NewSqurlMux()
// Middleware execution order:
mux.Use(middleware.Recovery()) // 1. Catch panics first
mux.Use(middleware.Logger()) // 2. Log all requests
mux.Use(middleware.CORS()) // 3. Handle CORS
mux.Use(middleware.RateLimit()) // 4. Rate limiting
mux.Use(middleware.Auth()) // 5. Authentication last
// Request flow:
// Request → Recovery → Logger → CORS → RateLimit → Auth → Handler
// Response ← Recovery ← Logger ← CORS ← RateLimit ← Auth ← Handler
mux.HandleFunc("/api/data", dataHandler)
http.ListenAndServe(":8080", mux)
}Recommended Order:
- Recovery (catch panics)
- Logging (log all requests)
- CORS (handle preflight requests)
- Compression (compress responses)
- Rate limiting (prevent abuse)
- Authentication (verify users)
- Authorization (check permissions)
Conditional Middleware
Apply middleware conditionally based on routes or conditions
package main
import (
"net/http"
"strings"
"github.com/squirrel-land/squirrel"
)
// Conditional middleware wrapper
func conditionalMiddleware(condition func(*http.Request) bool, middleware squirrel.Middleware) squirrel.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if condition(r) {
middleware(next).ServeHTTP(w, r)
} else {
next.ServeHTTP(w, r)
}
})
}
}
func main() {
mux := squirrel.NewSqurlMux()
// Apply auth middleware only to API routes
apiOnlyAuth := conditionalMiddleware(
func(r *http.Request) bool {
return strings.HasPrefix(r.URL.Path, "/api/")
},
authMiddleware,
)
mux.Use(apiOnlyAuth)
// Public routes (no auth required)
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/about", aboutHandler)
// API routes (auth required)
mux.HandleFunc("/api/users", usersHandler)
mux.HandleFunc("/api/posts", postsHandler)
http.ListenAndServe(":8080", mux)
}
// Skip middleware for specific paths
func skipPaths(paths []string, middleware squirrel.Middleware) squirrel.Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
for _, path := range paths {
if r.URL.Path == path {
next.ServeHTTP(w, r)
return
}
}
middleware(next).ServeHTTP(w, r)
})
}
}
// Usage: Skip auth for login and register endpoints
// mux.Use(skipPaths([]string{"/login", "/register"}, authMiddleware))Best Practices
Tips for effective middleware usage
Keep middleware focused
Each middleware should have a single responsibility (logging, auth, etc.)
Handle errors gracefully
Always provide meaningful error responses and dont let panics crash your server
Use context for request-scoped data
Store user information, request IDs, etc. in the request context
Test middleware independently
Write unit tests for your middleware functions to ensure they work correctly