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_golangrequire (
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: