Project ⏱ 10 min read

08 · Libraries & Integrations

Every external dependency used in the LP services — what it is and how it's used

Fiber — HTTP Framework

Fiber is an Express-inspired web framework. Fast and lightweight.

// Create app with config
app := fiber.New(fiber.Config{
  ReadTimeout:  10 * time.Second,
  ErrorHandler: middleware.ErrorHandlerMiddleware(log),
})

// Register middleware
app.Use(middleware.RequestIDMiddleware())

// Define routes
app.Get("/health", healthHandler.Health)

// Start server
app.Listen(":8080")

Handler signature: func(c *fiber.Ctx) error. Read params with c.Params("id"), body with c.BodyParser(&req), respond with c.JSON(data).

GORM — PostgreSQL ORM

Used in lp-coupon-api. Maps Go structs to database tables.

// Model with GORM tags
type Coupon struct {
  ID        string    `gorm:"primaryKey"`
  Name      string    `gorm:"not null"`
  Status    string
  CreatedAt time.Time `gorm:"autoCreateTime"`
}

// Create
db.Create(&coupon)

// Query
var coupon Coupon
db.Where("id = ?", id).First(&coupon)

// Update
db.Model(&coupon).Updates(map[string]interface{}{"status": "inactive"})

// GORM transaction
db.Transaction(func(tx *gorm.DB) error {
  if err := tx.Create(&coupon).Error; err != nil {
    return err // rollback
  }
  return nil // commit
})

MongoDB Driver — lp-reward-store-api

// Insert
collection.InsertOne(ctx, document)

// Find with filter
filter := bson.M{"status": "active"}
cursor, err := collection.Find(ctx, filter)

// Filter with $NOW (server-side, not time.Now())
filter := bson.M{
  "expiry_date": bson.M{"$gt": "$$NOW"}, // MongoDB system variable
}
Important: Use $$NOW (MongoDB system variable) for date comparisons — not time.Now() from Go. This avoids clock skew between app servers.

Redis — Caching & Rate Limiting

// Client setup (in main.go)
rdb := redis.NewClient(&redis.Options{
  Addr:     "localhost:6379",
  Password: "",
  DB:       0,
})

// Cache get
val, err := rdb.Get(ctx, "RewardStore|RewardStore:"+id).Result()
if err == redis.Nil {
  // cache miss — fetch from DB
}

// Cache set with TTL
rdb.Set(ctx, cacheKey, data, 5*time.Minute)

// Atomic multi-key delete (MULTI/EXEC = all-or-nothing)
pipe := rdb.TxPipeline()
pipe.Del(ctx, key1)
pipe.Del(ctx, key2)
_, err = pipe.Exec(ctx)

Cache key format: helpers.CacheKey("RewardStore|{Collection}", id)

Kafka Producer

// Initialized in routes.go when KAFKA_BOOTSTRAP_SERVER is set
producer, err := clients.NewKafkaProducer(
  cfg.Kafka.BootstrapServer,
  cfg.Kafka.BrokerCertContent,
  // ... TLS certs
  cfg.Kafka.UpdateUserCouponTopic,
  log.Zlogger,
)

// Publish a message (in service layer)
err := kafkaProducer.Produce(ctx, key, valueBytes)

Kafka initialization is guarded — if KAFKA_BOOTSTRAP_SERVER is empty, the producer is nil and events are skipped silently. This allows local development without Kafka.

ECI HTTP Client

ECI is an external service. The client wraps standard Go HTTP calls with auth headers.

// Initialized in routes.go
eciClient := clients.NewECIClient(cfg.ECI.URL, cfg.ECI.Key)

// Used in service layer
resp, err := eciClient.GetCustomer(ctx, customerID)
if err != nil {
  return nil, fmt.Errorf("ECI GetCustomer: %w", err)
}

Viper — Configuration

// config/config.go — load from env vars
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()

// Set defaults
viper.SetDefault("server.port", "8080")
viper.SetDefault("rate_limit.max_requests", 100)

// Access config
cfg.Server.Port         // typed struct
cfg.Kafka.BootstrapServer

All config comes from environment variables. See .env.example for the full list. Never hardcode values — always use config.

Retry — pkg/retryer

// Configure
retryFn := retryer.NewBackOffDelay(&retryer.Config{
  Attempts: 3,
  Delay:    500 * time.Millisecond,
})

// Use
err := retryFn(ctx, log, func() error {
  return externalService.Call(ctx)
})

Two strategies: NewBackOffDelay (exponential) and NewFixedDelay. Use backoff for external APIs, fixed for predictable retries.

Rate Limiting — pkg/ratelimit + middleware

// Key generation (pkg/ratelimit)
keys := ratelimit.NewKeys("ILPC014")
// keys.Counter = "CouponAPI|RateLimit|ILPC014|counter"
// keys.Lock    = "CouponAPI|RateLimit|ILPC014|lock"

// Apply to a route (routes.go)
userCoupon.Post("/bulk",
  rateLimiter.WithKeys(ratelimit.NewKeys("ILPC014")),
  userCouponHandler.BulkIssueCoupon,
)

Rate limits are per-route, identified by the ILPC code. The Redis-backed counter increments per request; the lock key blocks when the limit is exceeded.

Key Takeaways