gin

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: gin for comprehensive documentation.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "gin" with this command: npx skills add claude-dev-suite/claude-dev-suite/claude-dev-suite-claude-dev-suite-gin

Gin Core Knowledge

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: gin for comprehensive documentation.

Basic Setup

package main

import ( "github.com/gin-gonic/gin" "net/http" )

func main() { r := gin.Default() // Logger and Recovery middleware

r.GET("/", func(c *gin.Context) {
    c.String(http.StatusOK, "Hello, World!")
})

r.Run(":8080")

}

Routing

Basic Routes

r := gin.Default()

r.GET("/users", listUsers) r.GET("/users/:id", getUser) r.POST("/users", createUser) r.PUT("/users/:id", updateUser) r.DELETE("/users/:id", deleteUser)

// Any HTTP method r.Any("/any", handleAny)

// NoRoute handler r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{"error": "not found"}) })

Path Parameters

// Single parameter r.GET("/users/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, gin.H{"id": id}) })

// Wildcard r.GET("/files/*filepath", func(c *gin.Context) { filepath := c.Param("filepath") c.String(http.StatusOK, "File: %s", filepath) })

Route Groups

api := r.Group("/api") { v1 := api.Group("/v1") { v1.GET("/users", listUsersV1) v1.POST("/users", createUserV1) }

v2 := api.Group("/v2")
{
    v2.GET("/users", listUsersV2)
    v2.POST("/users", createUserV2)
}

}

Request Binding

JSON Binding

type CreateUserRequest struct { Name string json:"name" binding:"required" Email string json:"email" binding:"required,email" Age int json:"age" binding:"gte=0,lte=130" }

func createUser(c *gin.Context) { var req CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }

c.JSON(http.StatusCreated, gin.H{
    "name":  req.Name,
    "email": req.Email,
})

}

Query Binding

type ListUsersQuery struct { Page int form:"page" binding:"gte=1" PerPage int form:"per_page" binding:"gte=1,lte=100" Sort string form:"sort" }

func listUsers(c *gin.Context) { var query ListUsersQuery query.Page = 1 query.PerPage = 20

if err := c.ShouldBindQuery(&query); err != nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
    return
}

c.JSON(http.StatusOK, gin.H{
    "page":     query.Page,
    "per_page": query.PerPage,
})

}

Form Binding

type LoginForm struct { Username string form:"username" binding:"required" Password string form:"password" binding:"required" }

func login(c *gin.Context) { var form LoginForm if err := c.ShouldBind(&form); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }

c.JSON(http.StatusOK, gin.H{"username": form.Username})

}

URI Binding

type UserURI struct { ID uint uri:"id" binding:"required" }

func getUser(c *gin.Context) { var uri UserURI if err := c.ShouldBindUri(&uri); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }

c.JSON(http.StatusOK, gin.H{"id": uri.ID})

}

Validation

Custom Validator

import "github.com/go-playground/validator/v10"

var validateUsername validator.Func = func(fl validator.FieldLevel) bool { username := fl.Field().String() return len(username) >= 3 && len(username) <= 20 }

func main() { r := gin.Default()

if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    v.RegisterValidation("username", validateUsername)
}

r.Run(":8080")

}

type RegisterRequest struct { Username string json:"username" binding:"required,username" Email string json:"email" binding:"required,email" }

Middleware

Built-in Middleware

r := gin.New()

r.Use(gin.Logger()) r.Use(gin.Recovery())

r.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { return fmt.Sprintf("%s - [%s] %s %s %d %s\n", param.ClientIP, param.TimeStamp.Format(time.RFC1123), param.Method, param.Path, param.StatusCode, param.Latency, ) }))

Custom Middleware

func TimingMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() duration := time.Since(start) c.Header("X-Response-Time", duration.String()) } }

func main() { r := gin.Default() r.Use(TimingMiddleware()) }

Authentication Middleware

func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { token := c.GetHeader("Authorization")

    if token == "" {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "missing token"})
        c.Abort()
        return
    }

    if !strings.HasPrefix(token, "Bearer ") {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid format"})
        c.Abort()
        return
    }

    tokenString := token[7:]
    user, err := validateToken(tokenString)
    if err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
        c.Abort()
        return
    }

    c.Set("user", user)
    c.Next()
}

}

protected := r.Group("/api") protected.Use(AuthMiddleware()) { protected.GET("/me", getMe) }

func getMe(c *gin.Context) { user, _ := c.Get("user") c.JSON(http.StatusOK, user) }

CORS Middleware

import "github.com/gin-contrib/cors"

func main() { r := gin.Default()

r.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"https://example.com"},
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
    AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
    AllowCredentials: true,
    MaxAge:           12 * time.Hour,
}))

r.Run(":8080")

}

Response Rendering

JSON Response

func getUser(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "id": 1, "name": "Alice", }) }

type User struct { ID uint json:"id" Name string json:"name" }

func getUser(c *gin.Context) { user := User{ID: 1, Name: "Alice"} c.JSON(http.StatusOK, user) }

Other Formats

c.XML(http.StatusOK, gin.H{"message": "hello"}) c.YAML(http.StatusOK, gin.H{"message": "hello"}) c.String(http.StatusOK, "Hello, %s!", name) c.Redirect(http.StatusMovedPermanently, "https://example.com") c.File("./static/file.txt")

Production Readiness

Configuration

func main() { gin.SetMode(gin.ReleaseMode)

r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{
    Formatter: jsonLogFormatter,
    Output:    os.Stdout,
}))

r.Run(":8080")

}

func jsonLogFormatter(param gin.LogFormatterParams) string { log := map[string]interface{}{ "timestamp": param.TimeStamp.Format(time.RFC3339), "status": param.StatusCode, "latency": param.Latency.Milliseconds(), "method": param.Method, "path": param.Path, } b, _ := json.Marshal(log) return string(b) + "\n" }

Health Checks

func healthHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"status": "healthy"}) }

func readyHandler(db *sql.DB) gin.HandlerFunc { return func(c gin.Context) { ctx, cancel := context.WithTimeout(c.Request.Context(), 2time.Second) defer cancel()

    if err := db.PingContext(ctx); err != nil {
        c.JSON(http.StatusServiceUnavailable, gin.H{
            "status":   "not ready",
            "database": "disconnected",
        })
        return
    }

    c.JSON(http.StatusOK, gin.H{
        "status":   "ready",
        "database": "connected",
    })
}

}

Graceful Shutdown

func main() { r := gin.Default() r.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "Hello") })

srv := &#x26;http.Server{
    Addr:    ":8080",
    Handler: r,
}

go func() {
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatalf("listen: %s\n", err)
    }
}()

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
&#x3C;-quit
log.Println("Shutting down...")

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

if err := srv.Shutdown(ctx); err != nil {
    log.Fatal("Server forced to shutdown:", err)
}

}

Checklist

  • gin.ReleaseMode for production

  • CORS configured

  • Authentication middleware

  • Request validation

  • Structured JSON logging

  • Health/readiness endpoints

  • Graceful shutdown

  • Rate limiting

When NOT to Use This Skill

  • Echo projects - Echo has different middleware patterns

  • Fiber projects - Fiber is Express-like with different API

  • Chi projects - Chi is stdlib-compatible router

  • gRPC services - Use gRPC framework directly

  • Minimal stdlib apps - Gin adds dependency overhead

Anti-Patterns

Anti-Pattern Why It's Bad Solution

Using gin.Default() without knowing Includes logger/recovery you may not want Use gin.New() and add middleware explicitly

Not checking binding errors Invalid data processed Always check ShouldBindJSON() error

Using c.String() for JSON Manual serialization error-prone Use c.JSON() instead

Global variables for config Hard to test Pass config via context or dependency injection

Missing validation tags No input validation Use binding:"required,email" tags

Not setting gin.ReleaseMode Debug logs in production Set gin.SetMode(gin.ReleaseMode)

Quick Troubleshooting

Problem Diagnosis Fix

Route not found (404) Route not registered Check router setup and method

Binding fails silently Not checking error Check ShouldBindJSON() return value

CORS errors Not configured Use gin-contrib/cors middleware

Middleware not executing Wrong order Place middleware before routes

Panic in handler No recovery middleware Use gin.Recovery()

Slow JSON serialization Large response Use c.Stream() or pagination

Reference Documentation

  • Routing

  • Binding

  • Middleware

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

cron-scheduling

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

token-optimization

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

webrtc

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

react-19

No summary provided by upstream source.

Repository SourceNeeds Review