$ npx rulesync-cli pull✓ Wrote CLAUDE.md (2 rulesets)# Coding Standards- Always use async/await- Prefer named exports
Rule Writing

CLAUDE.md for Gin (Go) Web Framework

Gin is Go's most popular web framework — AI generates Express patterns in Go syntax. Rules for Gin middleware, binding, validation, error handling, and route groups.

7 min read·November 6, 2025

Gin is Go-native — AI generates Express patterns in Go syntax instead

gin.Context, ShouldBind validation, route groups, middleware, and graceful shutdown

Why Gin Needs Framework-Specific Rules Beyond Go Rules

Gin is Go's most popular HTTP framework — lightweight, fast, and built around middleware chains. Your Go language rules cover error handling, interfaces, and testing. But Gin has its own patterns that AI assistants miss: the gin.Context for request/response handling, ShouldBind for validation, route groups for organization, and middleware functions for cross-cutting concerns. AI generates raw net/http patterns or Express-style code adapted to Go syntax.

The most common AI failures: using c.JSON directly for errors instead of custom error middleware, ignoring ShouldBind in favor of manual JSON parsing, creating flat route lists instead of groups, not propagating request context (c.Request.Context()), and missing graceful shutdown patterns.

These rules layer on top of the Go language rules — they cover Gin-specific patterns. Use both together.

Rule 1: Proper gin.Context Usage

The rule: 'gin.Context is the core of every handler — it carries the request, response writer, route parameters, middleware values, and abort state. Access path params with c.Param("id"). Access query params with c.Query("page") or c.DefaultQuery("page", "1"). Access headers with c.GetHeader("Authorization"). Store request-scoped values with c.Set("user", user) and c.Get("user"). Never pass gin.Context to service functions — extract the data and pass primitives.'

For request context propagation: 'Pass c.Request.Context() to all downstream operations: db.QueryContext(c.Request.Context(), ...), http.NewRequestWithContext(c.Request.Context(), ...). This propagates cancellation — when the client disconnects, downstream operations are cancelled. Never use context.Background() in handlers — always c.Request.Context().'

For response: 'Use c.JSON(statusCode, obj) for JSON responses. Use c.AbortWithStatusJSON for error responses that should stop the middleware chain. Never call c.JSON after c.Abort — it sends duplicate responses. Check c.IsAborted() before writing if other middleware might have aborted.'

  • c.Param for path params — c.Query for query — c.GetHeader for headers
  • c.Set/c.Get for request-scoped values between middleware and handlers
  • c.Request.Context() for downstream propagation — never context.Background()
  • c.JSON for responses — c.AbortWithStatusJSON for error + stop chain
  • Never pass gin.Context to services — extract data, pass primitives
💡 Never Pass gin.Context

gin.Context carries the HTTP request, response, and middleware state. Extract what you need (user ID, query params) and pass primitives to services. Passing gin.Context couples your business logic to the web framework.

Rule 2: ShouldBind for Input Validation

The rule: 'Use ShouldBind methods for all input parsing and validation: c.ShouldBindJSON(&req) for JSON body, c.ShouldBindQuery(&req) for query params, c.ShouldBindUri(&req) for path params. Define request structs with binding tags: type CreateUserRequest struct { Name string `json:"name" binding:"required,min=1"` Email string `json:"email" binding:"required,email"` }. ShouldBind validates and returns errors — never parse JSON manually.'

For custom validation: 'Register custom validators: v.RegisterValidation("phone", validatePhone). Use struct-level validation for cross-field checks: v.RegisterStructValidation(validateUser, CreateUserRequest{}). Return structured validation errors: map field names to error messages for the client.'

AI generates json.NewDecoder(c.Request.Body).Decode(&req) — manual parsing with no validation. ShouldBind does parsing + validation in one call, returns structured errors, and supports the full go-playground/validator tag set.

⚠️ ShouldBind, Not Decode

AI generates json.NewDecoder(c.Request.Body).Decode(&req) — manual parsing with no validation. ShouldBindJSON does parsing + validation in one call with go-playground/validator tags. One function replaces 10 lines.

Rule 3: Route Groups and Organization

The rule: 'Use route groups for logical organization and shared middleware: api := r.Group("/api", authMiddleware()). Nest groups: v1 := api.Group("/v1"); users := v1.Group("/users"). Apply middleware to groups: admin.Use(requireAdmin()). Each resource gets its own group and handler file: routes/users.go, routes/orders.go.'

For handler organization: 'Define handlers as methods on a handler struct: type UserHandler struct { service UserService }. Register routes in a method: func (h *UserHandler) RegisterRoutes(rg *gin.RouterGroup). This keeps handlers testable — inject mock services for testing without starting the server.'

For versioning: 'Group by API version: v1 := api.Group("/v1"), v2 := api.Group("/v2"). Keep v1 handlers running while developing v2. Version in the URL path — not headers — for simplicity and cacheability.'

Rule 4: Middleware Patterns

The rule: 'Middleware functions have the signature func(c *gin.Context). Call c.Next() to continue the chain. Code before c.Next() runs on the request path, code after runs on the response path. Use c.Abort() to stop the chain (for auth failures, rate limits). Use c.Set() to pass data to subsequent handlers.'

For standard middleware: 'Use gin.Logger() for request logging (built-in). Use gin.Recovery() for panic recovery (built-in). Create custom middleware for: authentication (verify JWT, set user on context), request ID (generate UUID, add to context and response header), CORS (set headers, handle preflight), rate limiting (check rate, abort if exceeded).'

For error handling middleware: 'Create a centralized error handler middleware that runs after handlers: r.Use(errorHandler()). Handlers set errors on the context: c.Error(appError). The error middleware reads c.Errors, formats them consistently, and sends the response. This eliminates redundant error handling in every handler.'

  • Before c.Next() = request path — after c.Next() = response path
  • c.Abort() stops the chain — c.AbortWithStatusJSON for error + stop
  • gin.Logger() + gin.Recovery() as baseline — always include both
  • Auth middleware: verify token → c.Set("user", user) → c.Next()
  • Error middleware: read c.Errors → format → respond — after all handlers

Rule 5: Graceful Shutdown and Production Patterns

The rule: 'Never use r.Run() in production — it doesn't support graceful shutdown. Use http.Server with r as handler: srv := &http.Server{Addr: ":8080", Handler: r}. Listen for SIGINT/SIGTERM: signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM). On signal: ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second); srv.Shutdown(ctx). This finishes in-flight requests before stopping.'

For health checks: 'Add a health endpoint: r.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{"status": "ok"}) }). Check dependencies in the health endpoint: database ping, cache ping. Kubernetes/load balancers probe this endpoint to determine if the instance can receive traffic.'

For production config: 'Set gin.SetMode(gin.ReleaseMode) in production — it disables debug logging and colored output. Use environment variables for port, database URL, and secrets. Use trusted proxies: r.SetTrustedProxies([]string{"10.0.0.0/8"}) for correct client IP detection behind a load balancer.'

ℹ️ Never r.Run() in Prod

r.Run() doesn't support graceful shutdown — it kills in-flight requests on SIGTERM. Use http.Server with signal handling and srv.Shutdown(ctx). This finishes requests before stopping — critical for zero-downtime deploys.

Complete Gin Rules Template

Consolidated rules for Gin web framework projects (use alongside Go language rules).

  • gin.Context: c.Param, c.Query, c.Set/c.Get — never pass context to services
  • c.Request.Context() for downstream propagation — never context.Background() in handlers
  • ShouldBindJSON/ShouldBindQuery for validation — never manual JSON decode
  • Route groups: r.Group with middleware — handler structs for testability
  • Middleware: before/after c.Next() — c.Abort for early termination — centralized error handler
  • http.Server for graceful shutdown — never r.Run() in production
  • gin.ReleaseMode — trusted proxies — /health endpoint for probes
  • testify + httptest — gin.CreateTestContext for handler unit tests