Claude-skill-registry go-api-reviewer

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/go-api-reviewer" ~/.claude/skills/majiayu000-claude-skill-registry-go-api-reviewer && rm -rf "$T"
manifest: skills/data/go-api-reviewer/SKILL.md
source content

Go API Reviewer Skill

Purpose

Reviews Go API projects using Gin, Echo, Fiber, or Chi for routing, middleware, and API patterns.

When to Use

  • Go REST API code review
  • Gin/Echo/Fiber/Chi project review
  • Middleware implementation review
  • API request/response handling
  • API documentation review

Project Detection

  • github.com/gin-gonic/gin
    import
  • github.com/labstack/echo
    import
  • github.com/gofiber/fiber
    import
  • github.com/go-chi/chi
    import
  • handlers/
    ,
    routes/
    ,
    middleware/
    directories

Workflow

Step 1: Analyze Project

**Framework**: Gin v1.9+
**Router**: Group-based routing
**Middleware**: Auth, CORS, Logger, Recovery
**Validation**: go-playground/validator
**Docs**: Swagger/OpenAPI

Step 2: Select Review Areas

AskUserQuestion:

"Which areas to review?"
Options:
- Full API review (recommended)
- Router and handler patterns
- Middleware implementation
- Request validation
- Error handling and responses
multiSelect: true

Detection Rules

Router Organization

CheckRecommendationSeverity
All routes in mainUse router groupsMEDIUM
No versioningAdd /api/v1 prefixMEDIUM
Inconsistent namingFollow REST conventionsLOW
No route groupingGroup by resourceMEDIUM
// BAD: All routes in main.go
func main() {
    r := gin.Default()
    r.GET("/users", getUsers)
    r.POST("/users", createUser)
    r.GET("/users/:id", getUser)
    r.GET("/products", getProducts)
    // ... 50 more routes
}

// GOOD: Organized route groups (Gin)
func SetupRouter() *gin.Engine {
    r := gin.Default()

    api := r.Group("/api/v1")
    {
        users := api.Group("/users")
        {
            users.GET("", listUsers)
            users.POST("", createUser)
            users.GET("/:id", getUser)
            users.PUT("/:id", updateUser)
            users.DELETE("/:id", deleteUser)
        }

        products := api.Group("/products")
        {
            products.GET("", listProducts)
            products.GET("/:id", getProduct)
        }
    }

    return r
}

// GOOD: Separate route files
// routes/users.go
func RegisterUserRoutes(rg *gin.RouterGroup) {
    users := rg.Group("/users")
    h := NewUserHandler()

    users.GET("", h.List)
    users.POST("", h.Create)
    users.GET("/:id", h.Get)
}

Middleware Patterns

CheckRecommendationSeverity
Auth in handlerExtract to middlewareHIGH
No recovery middlewareAdd panic recoveryHIGH
No request IDAdd request ID middlewareMEDIUM
Middleware order wrongOrder: Logger → Recovery → AuthMEDIUM
// GOOD: Middleware stack (Gin)
func SetupMiddleware(r *gin.Engine) {
    // Order matters!
    r.Use(gin.Logger())
    r.Use(gin.Recovery())
    r.Use(RequestIDMiddleware())
    r.Use(CORSMiddleware())
}

// GOOD: Auth middleware
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")
        if token == "" {
            c.AbortWithStatusJSON(401, gin.H{"error": "unauthorized"})
            return
        }

        claims, err := ValidateToken(token)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
            return
        }

        c.Set("user_id", claims.UserID)
        c.Next()
    }
}

// Usage
api := r.Group("/api/v1")
api.Use(AuthMiddleware())

// GOOD: Request ID middleware
func RequestIDMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        requestID := c.GetHeader("X-Request-ID")
        if requestID == "" {
            requestID = uuid.New().String()
        }
        c.Set("request_id", requestID)
        c.Header("X-Request-ID", requestID)
        c.Next()
    }
}

Request Validation

CheckRecommendationSeverity
Manual validationUse validator tagsMEDIUM
No binding errorsReturn validation errorsHIGH
No request DTOsDefine request structsMEDIUM
Missing required fieldsAdd binding:"required"HIGH
// GOOD: Request struct with validation
type CreateUserRequest struct {
    Name     string `json:"name" binding:"required,min=1,max=100"`
    Email    string `json:"email" binding:"required,email"`
    Age      int    `json:"age" binding:"gte=0,lte=150"`
    Role     string `json:"role" binding:"oneof=admin user guest"`
    Password string `json:"password" binding:"required,min=8"`
}

// GOOD: Handler with validation
func (h *UserHandler) Create(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{
            "error":   "validation_error",
            "details": formatValidationErrors(err),
        })
        return
    }

    user, err := h.service.Create(c.Request.Context(), &req)
    if err != nil {
        handleError(c, err)
        return
    }

    c.JSON(201, user)
}

// GOOD: Format validation errors
func formatValidationErrors(err error) map[string]string {
    errors := make(map[string]string)

    var ve validator.ValidationErrors
    if errors.As(err, &ve) {
        for _, e := range ve {
            field := strings.ToLower(e.Field())
            errors[field] = getErrorMessage(e)
        }
    }

    return errors
}

Error Handling

CheckRecommendationSeverity
Inconsistent error formatUse standard error responseHIGH
Internal errors exposedHide implementation detailsHIGH
No error codesAdd error codesMEDIUM
HTTP status inconsistentFollow REST conventionsMEDIUM
// GOOD: Standard error response
type ErrorResponse struct {
    Error   string            `json:"error"`
    Code    string            `json:"code,omitempty"`
    Details map[string]string `json:"details,omitempty"`
}

// GOOD: Custom errors
var (
    ErrNotFound     = &AppError{Code: "NOT_FOUND", Status: 404}
    ErrUnauthorized = &AppError{Code: "UNAUTHORIZED", Status: 401}
    ErrConflict     = &AppError{Code: "CONFLICT", Status: 409}
)

type AppError struct {
    Code    string
    Status  int
    Message string
}

func (e *AppError) Error() string {
    return e.Message
}

// GOOD: Error handler
func handleError(c *gin.Context, err error) {
    var appErr *AppError
    if errors.As(err, &appErr) {
        c.JSON(appErr.Status, ErrorResponse{
            Error: appErr.Message,
            Code:  appErr.Code,
        })
        return
    }

    // Log internal error, return generic message
    log.Printf("internal error: %v", err)
    c.JSON(500, ErrorResponse{
        Error: "Internal server error",
        Code:  "INTERNAL_ERROR",
    })
}

Framework-Specific (Echo)

// Echo example
func SetupEcho() *echo.Echo {
    e := echo.New()

    e.Use(middleware.Logger())
    e.Use(middleware.Recover())
    e.Use(middleware.RequestID())
    e.Use(middleware.CORS())

    api := e.Group("/api/v1")
    api.Use(AuthMiddleware)

    RegisterUserRoutes(api)

    return e
}

// Echo handler
func (h *UserHandler) Create(c echo.Context) error {
    var req CreateUserRequest
    if err := c.Bind(&req); err != nil {
        return echo.NewHTTPError(400, "invalid request")
    }

    if err := c.Validate(&req); err != nil {
        return err
    }

    user, err := h.service.Create(c.Request().Context(), &req)
    if err != nil {
        return err
    }

    return c.JSON(201, user)
}

Response Template

## Go API Code Review Results

**Project**: [name]
**Framework**: Gin 1.9 | **Go**: 1.22

### Router Organization
| Status | File | Issue |
|--------|------|-------|
| MEDIUM | main.go | 40+ routes in single file |

### Middleware
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/user.go | Auth check in handler, not middleware |

### Validation
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/user.go:34 | No request validation |

### Error Handling
| Status | File | Issue |
|--------|------|-------|
| HIGH | handlers/product.go | Inconsistent error response format |

### Recommended Actions
1. [ ] Split routes into separate files by resource
2. [ ] Extract auth logic to middleware
3. [ ] Add request struct validation
4. [ ] Implement standard error response format

Best Practices

  1. Router: Group by resource, version API
  2. Middleware: Proper order, reusable
  3. Validation: Use validator tags
  4. Errors: Standard format, hide internals
  5. Docs: Generate OpenAPI from code

Integration

  • go-reviewer
    : General Go patterns
  • security-scanner
    : API security
  • api-documenter
    : OpenAPI documentation