EmailVerify LogoEmailVerify

Fiber

Email checker for Go Fiber. Email verification in Fiber HTTP handlers.

Integrate EmailVerify into your Fiber applications for blazing-fast email validation. This guide covers performance optimization, WebSocket support, and production monitoring.

Installation

Install Fiber framework and EmailVerify dependencies.

go get github.com/gofiber/fiber/v2
go get github.com/emailverify/emailverify-go
go get github.com/go-playground/validator/v10
go get github.com/go-redis/redis/v8
go get gorm.io/gorm
go get github.com/prometheus/client_golang
require (
    github.com/gofiber/fiber/v2 v2.50.0
    github.com/emailverify/emailverify-go v0.1.0
    github.com/go-playground/validator/v10 v10.15.0
    github.com/go-redis/redis/v8 v8.11.5
    gorm.io/gorm v1.25.0
    github.com/prometheus/client_golang v1.17.0
)

Configuration

Fiber App Setup

Configure your Fiber application with performance optimizations:

package config

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
    "github.com/gofiber/fiber/v2/middleware/compress"
    "github.com/gofiber/fiber/v2/middleware/helmet"
)

type AppConfig struct {
    APIKey      string
    DatabaseURL string
    RedisURL    string
    Port        string
}

func NewApp(cfg *AppConfig) *fiber.App {
    app := fiber.New(fiber.Config{
        // Performance optimizations
        Prefork: true,
        Network: fiber.NetworkTCP,

        // Streaming body size
        StreamRequestBody: true,
        BodyLimit:         10 * 1024 * 1024, // 10 MB

        // Disables keep-alive
        DisableKeepalive: false,

        // Case-sensitive routing
        CaseSensitive: true,

        // Strict routing
        StrictRoute: false,
    })

    // Middleware
    app.Use(helmet.New())
    app.Use(compress.New(compress.Config{
        Level: compress.LevelBestSpeed,
    }))
    app.Use(cors.New(cors.Config{
        AllowOrigins: "*",
        AllowMethods: "GET,POST,PUT,DELETE,OPTIONS",
    }))

    return app
}

Environment Configuration

Load configuration from environment:

package config

import (
    "os"
    "log"
)

func LoadConfig() *AppConfig {
    apiKey := os.Getenv("EMAILVERIFY_API_KEY")
    if apiKey == "" {
        log.Fatal("EMAILVERIFY_API_KEY is required")
    }

    return &AppConfig{
        APIKey:      apiKey,
        DatabaseURL: os.Getenv("DATABASE_URL"),
        RedisURL:    os.Getenv("REDIS_URL"),
        Port:        getEnv("PORT", "3000"),
    }
}

func getEnv(key, fallback string) string {
    if value := os.Getenv(key); value != "" {
        return value
    }
    return fallback
}

Middleware

Email Verification Middleware

Create custom middleware for request logging and validation:

package middleware

import (
    "github.com/gofiber/fiber/v2"
    "log/slog"
    "time"
)

func Logger() fiber.Handler {
    return func(c *fiber.Ctx) error {
        start := time.Now()

        // Process request
        err := c.Next()

        // Calculate execution time
        stop := time.Now()
        latency := stop.Sub(start)

        // Log request details
        slog.Info("HTTP Request",
            "method", c.Method(),
            "path", c.Path(),
            "status", c.Response().StatusCode(),
            "latency_ms", latency.Milliseconds(),
        )

        return err
    }
}

func ErrorHandler() fiber.ErrorHandler {
    return func(c *fiber.Ctx, err error) error {
        code := fiber.StatusInternalServerError

        if e, ok := err.(*fiber.Error); ok {
            code = e.Code
        }

        return c.Status(code).JSON(fiber.Map{
            "error": err.Error(),
            "code":  code,
        })
    }
}

CORS Middleware

Configure CORS for cross-origin requests:

package middleware

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/fiber/v2/middleware/cors"
)

func SetupCORS(app *fiber.App) {
    app.Use(cors.New(cors.Config{
        AllowOrigins:     "https://example.com,https://api.example.com",
        AllowMethods:     "GET,POST,PUT,DELETE,OPTIONS",
        AllowHeaders:     "Content-Type,Authorization",
        ExposeHeaders:    "Content-Length,X-RateLimit-Limit",
        MaxAge:           3600,
        AllowCredentials: true,
    }))
}

Routes

Single Email Verification Route

Define a route for verifying individual emails:

package routes

import (
    "github.com/gofiber/fiber/v2"
    "emailverify-app/handlers"
    "emailverify-app/services"
)

type VerifyEmailRequest struct {
    Email string `json:"email" validate:"required,email"`
}

type VerifyEmailResponse struct {
    Email     string      `json:"email"`
    Status    string      `json:"status"`
    Score     float64     `json:"score"`
    Details   interface{} `json:"details"`
    Cached    bool        `json:"cached"`
}

func SetupVerifyRoutes(app *fiber.App, service *services.EmailVerificationService) {
    api := app.Group("/api")
    verify := api.Group("/verify")

    verify.Post("/email", func(c *fiber.Ctx) error {
        var req VerifyEmailRequest

        if err := c.BodyParser(&req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "Invalid request",
            })
        }

        // Validate request
        if err := validate.Struct(req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "Validation failed",
                "details": err.Error(),
            })
        }

        // Verify email
        result, cached, err := service.VerifyEmail(c.Context(), req.Email)
        if err != nil {
            return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
                "error": "Verification failed",
            })
        }

        response := VerifyEmailResponse{
            Email:   req.Email,
            Status:  result.Status,
            Score:   result.Score,
            Details: result.Details,
            Cached:  cached,
        }

        return c.JSON(response)
    })
}

Bulk Email Verification Route

Create a route for batch verification:

type BulkVerifyRequest struct {
    Emails []string `json:"emails" validate:"required,min=1,max=1000,dive,email"`
}

type BulkVerifyResponse struct {
    Total     int                    `json:"total"`
    Verified  int                    `json:"verified"`
    Failed    int                    `json:"failed"`
    Results   []VerifyEmailResponse  `json:"results"`
    Duration  string                 `json:"duration"`
}

func SetupBulkRoutes(app *fiber.App, service *services.EmailVerificationService) {
    api := app.Group("/api")
    verify := api.Group("/verify")

    verify.Post("/bulk", func(c *fiber.Ctx) error {
        var req BulkVerifyRequest
        start := time.Now()

        if err := c.BodyParser(&req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "Invalid request",
            })
        }

        // Validate request
        if err := validate.Struct(req); err != nil {
            return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
                "error": "Validation failed",
            })
        }

        results := make([]VerifyEmailResponse, 0, len(req.Emails))
        failed := 0

        for _, email := range req.Emails {
            result, cached, err := service.VerifyEmail(c.Context(), email)
            if err != nil {
                failed++
                continue
            }

            results = append(results, VerifyEmailResponse{
                Email:   email,
                Status:  result.Status,
                Score:   result.Score,
                Details: result.Details,
                Cached:  cached,
            })
        }

        response := BulkVerifyResponse{
            Total:    len(req.Emails),
            Verified: len(results),
            Failed:   failed,
            Results:  results,
            Duration: time.Since(start).String(),
        }

        return c.JSON(response)
    })
}

Validation

Custom Validators

Extend validation with custom rules:

package validators

import (
    "github.com/go-playground/validator/v10"
    "strings"
)

type CustomValidator struct {
    validator *validator.Validate
}

func NewValidator() *CustomValidator {
    v := validator.New()

    v.RegisterValidationFunc("not_disposable", func(fl validator.FieldLevel) bool {
        email := fl.Field().String()
        domain := strings.Split(email, "@")[1]

        blockedDomains := map[string]bool{
            "tempmail.com":      true,
            "10minutemail.com":  true,
            "guerrillamail.com": true,
            "mailinator.com":    true,
        }

        return !blockedDomains[domain]
    })

    v.RegisterValidationFunc("corporate_email", func(fl validator.FieldLevel) bool {
        email := fl.Field().String()
        domain := strings.Split(email, "@")[1]

        // Only allow specific corporate domains
        allowedDomains := map[string]bool{
            "company.com": true,
        }

        return allowedDomains[domain]
    })

    return &CustomValidator{validator: v}
}

func (cv *CustomValidator) Validate(i interface{}) error {
    return cv.validator.Struct(i)
}

Database Integration

GORM with Fiber

Integrate GORM for database persistence:

package database

import (
    "gorm.io/driver/postgres"
    "gorm.io/gorm"
    "time"
)

type VerificationResult struct {
    ID            uint      `gorm:"primaryKey"`
    Email         string    `gorm:"uniqueIndex;not null;type:varchar(254)"`
    Status        string    `gorm:"not null;type:varchar(50)"`
    Score         float64
    Disposable    bool
    SmtpValid     bool
    FormatValid   bool
    EmailProvider string
    RiskLevel     string
    VerifiedAt    time.Time
    CachedAt      time.Time `gorm:"autoUpdateTime:milli"`
}

func NewDatabase(dsn string) (*gorm.DB, error) {
    db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
    if err != nil {
        return nil, err
    }

    // Enable connection pooling for performance
    sqlDB, err := db.DB()
    if err != nil {
        return nil, err
    }

    sqlDB.SetMaxOpenConns(25)
    sqlDB.SetMaxIdleConns(5)
    sqlDB.SetConnMaxLifetime(5 * time.Minute)

    // Auto migrate models
    if err := db.AutoMigrate(&VerificationResult{}); err != nil {
        return nil, err
    }

    return db, nil
}

Caching

Memory and Redis Caching

Implement multi-layer caching with memory and Redis:

package cache

import (
    "context"
    "encoding/json"
    "github.com/go-redis/redis/v8"
    "sync"
    "time"
)

type MemoryCache struct {
    data map[string]CacheEntry
    mu   sync.RWMutex
    ttl  time.Duration
}

type CacheEntry struct {
    Value     interface{}
    ExpiresAt time.Time
}

type HybridCache struct {
    memory *MemoryCache
    redis  *redis.Client
}

func NewHybridCache(redisURL string) (*HybridCache, error) {
    opts, _ := redis.ParseURL(redisURL)
    client := redis.NewClient(opts)

    return &HybridCache{
        memory: &MemoryCache{
            data: make(map[string]CacheEntry),
            ttl:  5 * time.Minute,
        },
        redis: client,
    }, nil
}

func (hc *HybridCache) Get(ctx context.Context, key string) (interface{}, error) {
    // Check memory cache first
    hc.memory.mu.RLock()
    entry, exists := hc.memory.data[key]
    hc.memory.mu.RUnlock()

    if exists && time.Now().Before(entry.ExpiresAt) {
        return entry.Value, nil
    }

    // Check Redis
    val, err := hc.redis.Get(ctx, key).Result()
    if err == nil {
        var data interface{}
        json.Unmarshal([]byte(val), &data)

        // Populate memory cache
        hc.memory.mu.Lock()
        hc.memory.data[key] = CacheEntry{
            Value:     data,
            ExpiresAt: time.Now().Add(hc.memory.ttl),
        }
        hc.memory.mu.Unlock()

        return data, nil
    }

    return nil, err
}

func (hc *HybridCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
    // Set in memory cache
    hc.memory.mu.Lock()
    hc.memory.data[key] = CacheEntry{
        Value:     value,
        ExpiresAt: time.Now().Add(hc.memory.ttl),
    }
    hc.memory.mu.Unlock()

    // Set in Redis
    data, _ := json.Marshal(value)
    return hc.redis.Set(ctx, key, data, ttl).Err()
}

Performance Optimization

Connection Pooling and Optimization

Optimize for high throughput:

package config

import (
    "github.com/gofiber/fiber/v2"
    "net"
    "time"
)

func OptimizePerformance(app *fiber.App) {
    // Tuning network settings
    app.Config.Network = fiber.NetworkTCP4
    app.Config.Concurrency = 256 * 1024 // Max concurrent connections

    // TCP tuning
    app.Listen(":3000", fiber.ListenConfig{
        ListenerNetwork: "tcp",
        SocketShutdown:  true,
        TcpKeepAlive:    30 * time.Second,
    })

    // Request body optimization
    app.Config.BodyLimit = 50 * 1024 * 1024 // 50 MB
    app.Config.ReadBufferSize = 16 * 1024   // 16 KB
    app.Config.WriteBufferSize = 16 * 1024  // 16 KB

    // Keep-alive settings
    app.Config.IdleTimeout = 75 * time.Second
    app.Config.ReadTimeout = 15 * time.Second
    app.Config.WriteTimeout = 15 * time.Second
}

Error Handling

Comprehensive Error Handling

Define consistent error responses:

package errors

import (
    "github.com/gofiber/fiber/v2"
)

type APIError struct {
    Code    int    `json:"code"`
    Message string `json:"message"`
    Details string `json:"details,omitempty"`
}

func BadRequest(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusBadRequest).JSON(APIError{
        Code:    fiber.StatusBadRequest,
        Message: message,
    })
}

func NotFound(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusNotFound).JSON(APIError{
        Code:    fiber.StatusNotFound,
        Message: message,
    })
}

func InternalError(c *fiber.Ctx, message string) error {
    return c.Status(fiber.StatusInternalServerError).JSON(APIError{
        Code:    fiber.StatusInternalServerError,
        Message: message,
    })
}

Testing

Integration Tests with httptest

Test your routes and handlers:

package handlers

import (
    "bytes"
    "encoding/json"
    "testing"
    "github.com/gofiber/fiber/v2"
    "github.com/stretchr/testify/assert"
)

func TestVerifyEmail(t *testing.T) {
    tests := []struct {
        name           string
        email          string
        expectedStatus int
    }{
        {
            name:           "Valid email",
            email:          "test@example.com",
            expectedStatus: fiber.StatusOK,
        },
        {
            name:           "Invalid email format",
            email:          "invalid",
            expectedStatus: fiber.StatusBadRequest,
        },
        {
            name:           "Empty email",
            email:          "",
            expectedStatus: fiber.StatusBadRequest,
        },
    }

    app := fiber.New()

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            body := VerifyEmailRequest{Email: tt.email}
            bodyBytes, _ := json.Marshal(body)

            req := fiber.AcquireRequest()
            req.Header.SetContentType(fiber.MIMEApplicationJSON)
            req.SetBodyStream(bytes.NewReader(bodyBytes), len(bodyBytes))

            // Test endpoint
            // assert.Equal(t, tt.expectedStatus, statusCode)
        })
    }
}

WebSocket Support

Real-time Email Verification

Implement WebSocket for real-time verification:

package routes

import (
    "github.com/gofiber/fiber/v2"
    "github.com/gofiber/websocket/v2"
    "emailverify-app/services"
)

func SetupWebSocketRoutes(app *fiber.App, service *services.EmailVerificationService) {
    ws := app.Group("/ws")

    ws.Get("/verify/:email", websocket.New(func(c *websocket.Conn) {
        email := c.Params("email")

        // Verify email
        result, cached, err := service.VerifyEmail(c.Context(), email)
        if err != nil {
            c.WriteJSON(fiber.Map{
                "error": err.Error(),
            })
            return
        }

        // Send result through WebSocket
        c.WriteJSON(fiber.Map{
            "email":  email,
            "status": result.Status,
            "score":  result.Score,
            "cached": cached,
        })

        // Graceful close
        c.Close()
    }))
}

Monitoring

Prometheus Metrics

Add Prometheus monitoring for production observability:

package monitoring

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
)

var (
    EmailVerifications = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "email_verifications_duration_seconds",
            Help:    "Email verification duration in seconds",
            Buckets: []float64{0.001, 0.01, 0.1, 1.0},
        },
        []string{"status"},
    )

    VerificationErrors = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "verification_errors_total",
            Help: "Total verification errors",
        },
        []string{"error_type"},
    )

    CacheHits = promauto.NewCounter(
        prometheus.CounterOpts{
            Name: "cache_hits_total",
            Help: "Total cache hits",
        },
    )

    CacheMisses = promauto.NewCounter(
        prometheus.CounterOpts{
            Name: "cache_misses_total",
            Help: "Total cache misses",
        },
    )
)

Production Deployment

Prefork Mode

Enable prefork mode for multi-process performance:

func main() {
    app := fiber.New(fiber.Config{
        Prefork: true,
        Network: fiber.NetworkTCP,
    })

    // Setup routes
    setupRoutes(app)

    // Listen with TLS for production
    app.ListenTLS(":443", "/path/to/cert.pem", "/path/to/key.pem", fiber.ListenConfig{
        GracefulShutdown: true,
    })
}

Docker Configuration

Production-ready Dockerfile:

# Build stage
FROM golang:1.21-alpine AS builder

WORKDIR /build

COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o app .

# Runtime stage
FROM alpine:latest

RUN apk add --no-cache ca-certificates

WORKDIR /app

COPY --from=builder /build/app .

EXPOSE 3000

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
    CMD wget -q -O /dev/null http://localhost:3000/health || exit 1

CMD ["./app"]

docker-compose.yml

Full stack configuration:

version: '3.8'

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      EMAILVERIFY_API_KEY: ${EMAILVERIFY_API_KEY}
      DATABASE_URL: postgres://user:password@db:5432/emailverify
      REDIS_URL: redis://redis:6379/0
    depends_on:
      - db
      - redis

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: emailverify
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data

  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml

volumes:
  postgres_data:
  redis_data:

On this page