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
}
$$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
- Fiber handler signature:
func(c *fiber.Ctx) error— same as Express but typed - GORM wraps SQL — use
db.Transaction()for all-or-nothing DB writes - MongoDB: use
$$NOWfor date filters, nottime.Now() - Redis:
TxPipeline().Exec()for atomic multi-key operations - Kafka init is optional — guarded by env var, nil-safe for local dev
- All config from env via Viper — never hardcode values
- Retry:
NewBackOffDelayfor external APIs; rate limit applied per route by ILPC code