Problem Statement
Build a production-ready REST API using Gin, the most popular Go web framework used by companies like Uber and Twitch.
Build a production-ready REST API using Gin, the most popular Go web framework used by companies like Uber and Twitch.
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// Production: use gin.New() for manual middleware control
r := gin.New()
r.Use(gin.Logger(), gin.Recovery())
// Routes
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "healthy"})
})
r.Run(":8080")
}type RegisterRequest struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=8"`
Age int `json:"age" binding:"gte=18,lte=120"`
}
func Register(c *gin.Context) {
var req RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": err.Error(),
})
return
}
// Process registration...
c.JSON(http.StatusCreated, gin.H{
"message": "User created",
"email": req.Email,
})
}func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(http.StatusUnauthorized, gin.H{
"error": "missing authorization header",
})
c.Abort()
return
}
// Validate token...
userID := validateToken(token)
c.Set("userID", userID)
c.Next()
}
}
// Apply to route group
admin := r.Group("/admin")
admin.Use(AuthMiddleware())
{
admin.GET("/users", ListUsers)
admin.DELETE("/users/:id", DeleteUser)
}func AsyncHandler(c *gin.Context) {
// ❌ WRONG: Using c directly in goroutine
// go func() {
// c.JSON(200, result) // RACE CONDITION!
// }()
// ✅ CORRECT: Copy context for goroutine
cCopy := c.Copy()
go func() {
result := expensiveOperation()
// Use cCopy, not c
log.Printf("Processed for %s", cCopy.ClientIP())
}()
c.JSON(http.StatusAccepted, gin.H{"status": "processing"})
}func JSONLogger() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Next()
log.Printf(`{"method":"%s","path":"%s","status":%d,"latency":"%v"}`,
c.Request.Method,
c.Request.URL.Path,
c.Writer.Status(),
time.Since(start),
)
}
}func TestRegister(t *testing.T) {
r := setupRouter()
body := `{"email":"test@example.com","password":"secret123","age":25}`
req := httptest.NewRequest("POST", "/register", strings.NewReader(body))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
assert.Equal(t, http.StatusCreated, w.Code)
}