EchoNextv1.4
Contrib Packages
Stable v1.4.8

Middleware

Learn about Middleware in EchoNext.

Middleware

EchoNext provides optional middleware helpers that complement Echo's built-in middleware.

Overview

The pkg/contrib/middleware package provides:

  • RequestID - Add correlation IDs to requests
  • Metrics - Simple request metrics collection
  • StructuredLogger - Enhanced logging with structured fields
  • OpenTelemetry - Automatic distributed tracing and metrics

These middleware complement Echo's built-in middleware rather than replacing them. Use Echo's excellent middleware for logging, recovery, CORS, etc., and add these helpers for additional functionality.

RequestID Middleware

Add unique request IDs for correlation and debugging:

import "github.com/abdussamadbello/echonext/pkg/contrib/middleware"

app := echonext.New()
app.Use(middleware.RequestID())

// Access in handlers
func handler(c echo.Context) error {
    requestID := middleware.GetRequestID(c)
    log.Printf("Processing request %s", requestID)
    return c.JSON(200, map[string]string{"request_id": requestID})
}

The Request ID is also added to the X-Request-ID header in responses.

Metrics Middleware

Collect basic request metrics:

import "github.com/abdussamadbello/echonext/pkg/contrib/middleware"

// Create metrics collector
metrics := middleware.NewMetrics()

// Add middleware
app.Use(middleware.MetricsMiddleware(metrics))

// Expose metrics endpoint
app.GET("/metrics", middleware.MetricsHandler(metrics))

Metrics collected:

  • Request count
  • Request duration
  • Status codes
  • Error rates

Structured Logger Middleware

Enhanced logging with custom fields:

app.Use(middleware.StructuredLogger(middleware.StructuredLoggerConfig{
    CustomFields: func(c echo.Context) map[string]interface{} {
        return map[string]interface{}{
            "request_id": middleware.GetRequestID(c),
            "user_agent": c.Request().UserAgent(),
            "user_id":    getUserID(c),
        }
    },
}))

OpenTelemetry Middleware

For comprehensive distributed tracing and observability, see the Observability Guide.

Quick example:

import (
    "context"
    "github.com/abdussamadbello/echonext/pkg/contrib/middleware"
)

func main() {
    ctx := context.Background()
    
    // Initialize OTEL
    shutdown, err := middleware.InitOTEL(ctx, middleware.DefaultOTELConfig())
    if err != nil {
        log.Printf("OTEL init failed: %v", err)
    } else {
        defer shutdown.Shutdown(ctx)
    }
    
    app := echonext.New()
    
    // Add OTEL middleware
    app.Use(middleware.RequestID())
    app.Use(middleware.OTELMiddleware("my-service"))
    
    // All requests are now automatically traced
    app.Start(":8080")
}

Features

  • Automatic Request Tracing - Every HTTP request creates a trace span
  • Traced HTTP Client - Track outgoing HTTP requests
  • Span Events - Add timeline markers within traces
  • Error Recording - Capture errors in traces
  • Request ID Correlation - Link requests and traces
  • Trace Context Propagation - W3C traceparent/tracestate headers

Traced HTTP Client

For service-to-service communication:

// Create traced client
tracedClient := middleware.NewTracedHTTPClient(
    middleware.WithClientTimeout(30 * time.Second),
)

func handler(c echo.Context) error {
    ctx := c.Request().Context()
    
    // Make traced outgoing request
    resp, err := tracedClient.Get(ctx, "https://api.example.com/data")
    if err != nil {
        middleware.RecordError(c, err)
        return echo.NewHTTPError(500, "API error")
    }
    defer resp.Body.Close()
    
    // Process response...
}

Environment Variables

Configure OTEL via environment variables:

export OTEL_SERVICE_NAME="my-service"
export OTEL_SERVICE_VERSION="1.0.0"
export OTEL_ENVIRONMENT="production"
export OTEL_EXPORTER_OTLP_ENDPOINT="localhost:4317"
export OTEL_INSECURE="false"
export OTEL_SAMPLE_RATE="0.1"
export OTEL_ENABLE_TRACING="true"
export OTEL_ENABLE_METRICS="true"

Helper Functions

Access trace information in handlers:

func handler(c echo.Context) error {
    // Get trace context
    traceID := middleware.GetTraceID(c)
    spanID := middleware.GetSpanID(c)
    requestID := middleware.GetRequestID(c)
    
    // Add span event
    middleware.AddSpanEvent(c, "processing order",
        attribute.String("order_id", "123"),
        attribute.Int("quantity", 5),
    )
    
    // Record error
    if err != nil {
        middleware.RecordError(c, err)
        return err
    }
    
    return c.JSON(200, data)
}

Combining Middleware

Use multiple middleware together:

import (
    "github.com/abdussamadbello/echonext"
    "github.com/abdussamadbello/echonext/pkg/contrib/middleware"
    echomw "github.com/labstack/echo/v4/middleware"
)

app := echonext.New()

// Echo built-in middleware
app.Use(echomw.Recover())
app.Use(echomw.CORS())
app.Use(echomw.Gzip())

// EchoNext contrib middleware
app.Use(middleware.RequestID())
app.Use(middleware.OTELMiddleware("my-service"))

// Metrics
metrics := middleware.NewMetrics()
app.Use(middleware.MetricsMiddleware(metrics))
app.GET("/metrics", middleware.MetricsHandler(metrics))

// Structured logging
app.Use(middleware.StructuredLogger(middleware.StructuredLoggerConfig{
    CustomFields: func(c echo.Context) map[string]interface{} {
        return map[string]interface{}{
            "trace_id":   middleware.GetTraceID(c),
            "request_id": middleware.GetRequestID(c),
        }
    },
}))

Custom Middleware

Create your own middleware following Echo's pattern:

func AuthMiddleware(apiKey string) echo.MiddlewareFunc {
    return func(next echo.HandlerFunc) echo.HandlerFunc {
        return func(c echo.Context) error {
            key := c.Request().Header.Get("X-API-Key")
            if key != apiKey {
                return echo.NewHTTPError(401, "invalid API key")
            }
            return next(c)
        }
    }
}

// Use it
app.Use(AuthMiddleware("secret-key"))

See Also

On this page