← Back to Cecep Azhar
Cecep Azhar #golang#middleware#http#web-api#fiber#echo

Middleware Pattern untuk Web API di Golang

Cecep Saeful Azhar • 16 Oct 2026
Middleware Pattern untuk Web API di Golang
Panduan lengkap membuat middleware di Go untuk HTTP API. Dari authentication, rate limiting, sampai custom logging middleware.

Middleware: Lapisan yang Menyelamatkan

Middleware di Go itu seperti security checkpoint di gedung: setiap request harus melewati beberapa lapisan sebelum sampai ke handler. Authentication, logging, rate limiting — semua bisa di-stack melalui middleware. Dan yang paling powerful: middleware bisa di-reuse di berbagai endpoint tanpa duplicate code.

Saya biasanya pakai Fiber atau Echo untuk web framework di Go, tapi middleware pattern-nya universal.

1. Middleware Dasar

func LoggerMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
        log.Printf("Completed %s %s in %v",
            r.Method, r.URL.Path, time.Since(start))
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/users", handleUsers)
    var handler http.Handler = mux
    handler = RecoveryMiddleware(handler)
    handler = AuthMiddleware(handler)
    handler = CORSMiddleware(handler)
    handler = LoggerMiddleware(handler)
    http.ListenAndServe(":8080", handler)
}

2. Authentication Middleware

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if isPublicRoute(r.URL.Path) {
            next.ServeHTTP(w, r)
            return
        }
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        claims, err := validateToken(token)
        if err != nil {
            http.Error(w, "Invalid token", http.StatusUnauthorized)
            return
        }
        ctx := context.WithValue(r.Context(), "user_id", claims.UserID)
        ctx = context.WithValue(ctx, "user_role", claims.Role)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

3. Rate Limiting Middleware

Dari pengalaman saya, rate limiting adalah middleware paling penting untuk production API. Tanpa ini, satu user bisa overload seluruh sistem:

type RateLimiter struct {
    limiter *rate.Limiter
}

func NewRateLimiter(rps rate.Limit, burst int) *RateLimiter {
    return &RateLimiter{
        limiter: rate.NewLimiter(rps, burst),
    }
}

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if !rl.limiter.Allow() {
            w.Header().Set("Retry-After", "1")
            http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func (rl *RateLimiter) PerUserMiddleware(next http.Handler) http.Handler {
    users := make(map[string]*rate.Limiter)
    var mu sync.Mutex
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        userID := getUserID(r)
        mu.Lock()
        if _, exists := users[userID]; !exists {
            users[userID] = rate.NewLimiter(10, 20)
        }
        limiter := users[userID]
        mu.Unlock()
        if !limiter.Allow() {
            http.Error(w, "Too many requests", http.StatusTooManyRequests)
            return
        }
        next.ServeHTTP(w, r)
    })
}

4. Panic Recovery Middleware

Di production, panic yang tidak di-recover bisa crash seluruh server. Ini middleware yang wajib ada:

func RecoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Error("Panic recovered",
                    "error", err,
                    "stack", string(debug.Stack()),
                    "path", r.URL.Path,
                )
                w.Header().Set("Content-Type", "application/json")
                w.WriteHeader(http.StatusInternalServerError)
                json.NewEncoder(w).Encode(map[string]string{
                    "error": "Internal server error",
                })
            }
        }()
        next.ServeHTTP(w, r)
    })
}

Best Practices dari Production

1. Middleware ordering matters. Recovery harus paling outer (first execute), authentication di atasnya, dan logging di luar semua.

2. Keep middleware small. Setiap middleware harus punya satu responsibility. Jangan gabungkan auth + logging di satu middleware.

3. Context propagation. Gunakan context untuk membawa data antar middleware. Jangan gunakan global variables — ini tidak thread-safe.

4. Test middleware independently. Setiap middleware bisa di-test dengan HTTP request/response yang di-mock tanpa perlu menjalankan full server.

← More articles from Cecep Azhar Central Content Hub Home →