Skip to main content
Kratos provides a powerful middleware system that works uniformly across HTTP and gRPC transports. Middleware allows you to intercept and process requests and responses in a composable way.

Core Concepts

Handler

The basic handler function that processes requests:
middleware/middleware.go:8
type Handler func(ctx context.Context, req any) (any, error)

Middleware

Middleware wraps a handler to add cross-cutting concerns:
middleware/middleware.go:11
type Middleware func(Handler) Handler

Middleware Chain

Chain multiple middleware together:
middleware/middleware.go:14-21
func Chain(m ...Middleware) Middleware {
    return func(next Handler) Handler {
        for i := len(m) - 1; i >= 0; i-- {
            next = m[i](next)
        }
        return next
    }
}

Creating Middleware

Basic Middleware

import (
    "context"
    "github.com/go-kratos/kratos/v2/middleware"
)

func MyMiddleware() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            // Before request processing
            log.Info("Before request")
            
            // Process request
            reply, err := handler(ctx, req)
            
            // After request processing
            log.Info("After request")
            
            return reply, err
        }
    }
}

Middleware with Options

func LoggingMiddleware(level string) middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            start := time.Now()
            
            reply, err := handler(ctx, req)
            
            duration := time.Since(start)
            if level == "debug" {
                log.Debugf("Request took: %v", duration)
            }
            
            return reply, err
        }
    }
}

Built-in Middleware

Kratos provides several production-ready middleware:

Recovery

Recover from panics and convert them to errors:
import "github.com/go-kratos/kratos/v2/middleware/recovery"

server := http.NewServer(
    http.Middleware(
        recovery.Recovery(),
    ),
)

Logging

Log request and response information:
import "github.com/go-kratos/kratos/v2/middleware/logging"

// Server-side logging
server := http.NewServer(
    http.Middleware(
        logging.Server(logger),
    ),
)

// Client-side logging  
client, err := http.NewClient(
    ctx,
    http.WithMiddleware(
        logging.Client(logger),
    ),
)

Metrics

Collect request metrics:
import "github.com/go-kratos/kratos/v2/middleware/metrics"

server := http.NewServer(
    http.Middleware(
        metrics.Server(
            metrics.WithSeconds(histogram),
            metrics.WithRequests(counter),
        ),
    ),
)

Tracing

Distributed tracing with OpenTelemetry:
import "github.com/go-kratos/kratos/v2/middleware/tracing"

server := http.NewServer(
    http.Middleware(
        tracing.Server(),
    ),
)

Validation

Validate request messages:
import "github.com/go-kratos/kratos/v2/middleware/validate"

server := grpc.NewServer(
    grpc.Middleware(
        validate.Validator(),
    ),
)

Rate Limiting

Limit request rate:
import (
    "github.com/go-kratos/kratos/v2/middleware/ratelimit"
    "golang.org/x/time/rate"
)

limiter := rate.NewLimiter(rate.Limit(100), 100) // 100 req/s

server := http.NewServer(
    http.Middleware(
        ratelimit.Server(ratelimit.WithLimiter(limiter)),
    ),
)

Circuit Breaker

Prevent cascading failures:
import (
    "github.com/go-kratos/kratos/v2/middleware/circuitbreaker"
    "github.com/go-kratos/aegis/circuitbreaker"
)

client, err := http.NewClient(
    ctx,
    http.WithMiddleware(
        circuitbreaker.Client(),
    ),
)

JWT Authentication

Validate JWT tokens:
import "github.com/go-kratos/kratos/v2/middleware/auth/jwt"

server := http.NewServer(
    http.Middleware(
        jwt.Server(
            func(token *jwtv5.Token) (interface{}, error) {
                return []byte("secret-key"), nil
            },
        ),
    ),
)

Metadata

Propagate metadata between services:
import "github.com/go-kratos/kratos/v2/middleware/metadata"

// Server extracts metadata from transport headers
server := http.NewServer(
    http.Middleware(
        metadata.Server(),
    ),
)

// Client injects metadata into transport headers
client, err := http.NewClient(
    ctx,
    http.WithMiddleware(
        metadata.Client(),
    ),
)

Applying Middleware

Global Middleware

Apply to all routes/methods:
server := http.NewServer(
    http.Middleware(
        recovery.Recovery(),
        logging.Server(logger),
        metrics.Server(),
    ),
)

Selective Middleware

Apply to specific paths:
// HTTP Server
httpSrv.Use("/api/*", 
    auth.JWT(jwtSecret),
    ratelimit.Server(),
)

// gRPC Server
grpcSrv.Use("/helloworld.v1.Greeter/*",
    auth.JWT(jwtSecret),
)

Selector Patterns

Supported patterns for selective middleware:
server.Use("/*", middleware1, middleware2)

Middleware Order

Middleware executes in the order specified:
server := http.NewServer(
    http.Middleware(
        recovery.Recovery(),  // 1. Outermost - catches panics
        logging.Server(),     // 2. Logs requests
        metrics.Server(),     // 3. Records metrics
        auth.JWT(),           // 4. Authenticates
        validate.Validator(), // 5. Validates input
        // Your handler executes here
    ),
)
Place recovery.Recovery() first to catch panics from other middleware.

Context Integration

Accessing Transport Info

import "github.com/go-kratos/kratos/v2/transport"

func MyMiddleware() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            if tr, ok := transport.FromServerContext(ctx); ok {
                log.Infof("Kind: %s, Operation: %s", 
                    tr.Kind(), tr.Operation())
            }
            return handler(ctx, req)
        }
    }
}

Setting Response Headers

func HeaderMiddleware() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            if tr, ok := transport.FromServerContext(ctx); ok {
                tr.ReplyHeader().Set("X-Service-Version", "v1.0.0")
            }
            return handler(ctx, req)
        }
    }
}

Advanced Patterns

Conditional Middleware

func ConditionalMiddleware(condition func(ctx context.Context) bool, mw middleware.Middleware) middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        wrapped := mw(handler)
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            if condition(ctx) {
                return wrapped(ctx, req)
            }
            return handler(ctx, req)
        }
    }
}

// Usage
server.Use("/*", ConditionalMiddleware(
    func(ctx context.Context) bool {
        // Only apply to production
        return os.Getenv("ENV") == "production"
    },
    ratelimit.Server(),
))

Middleware with State

type RequestCounter struct {
    count atomic.Int64
}

func (rc *RequestCounter) Middleware() middleware.Middleware {
    return func(handler middleware.Handler) middleware.Handler {
        return func(ctx context.Context, req interface{}) (interface{}, error) {
            rc.count.Add(1)
            return handler(ctx, req)
        }
    }
}

// Usage
counter := &RequestCounter{}
server := http.NewServer(
    http.Middleware(counter.Middleware()),
)

Best Practices

Always place recovery middleware first to catch panics from other middleware.
Only use middleware that’s necessary. Each middleware adds overhead.
Always check context cancellation and pass context through the chain.
Don’t perform long-running operations in middleware. Use goroutines if needed.
Apply expensive middleware only to routes that need it.

HTTP Transport

HTTP server and client

gRPC Transport

gRPC server and client

Logging

Logging system

Errors

Error handling

Build docs developers (and LLMs) love