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.mdsource 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
importgithub.com/gin-gonic/gin
importgithub.com/labstack/echo
importgithub.com/gofiber/fiber
importgithub.com/go-chi/chi
,handlers/
,routes/
directoriesmiddleware/
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
| Check | Recommendation | Severity |
|---|---|---|
| All routes in main | Use router groups | MEDIUM |
| No versioning | Add /api/v1 prefix | MEDIUM |
| Inconsistent naming | Follow REST conventions | LOW |
| No route grouping | Group by resource | MEDIUM |
// 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
| Check | Recommendation | Severity |
|---|---|---|
| Auth in handler | Extract to middleware | HIGH |
| No recovery middleware | Add panic recovery | HIGH |
| No request ID | Add request ID middleware | MEDIUM |
| Middleware order wrong | Order: Logger → Recovery → Auth | MEDIUM |
// 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
| Check | Recommendation | Severity |
|---|---|---|
| Manual validation | Use validator tags | MEDIUM |
| No binding errors | Return validation errors | HIGH |
| No request DTOs | Define request structs | MEDIUM |
| Missing required fields | Add 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
| Check | Recommendation | Severity |
|---|---|---|
| Inconsistent error format | Use standard error response | HIGH |
| Internal errors exposed | Hide implementation details | HIGH |
| No error codes | Add error codes | MEDIUM |
| HTTP status inconsistent | Follow REST conventions | MEDIUM |
// 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
- Router: Group by resource, version API
- Middleware: Proper order, reusable
- Validation: Use validator tags
- Errors: Standard format, hide internals
- Docs: Generate OpenAPI from code
Integration
: General Go patternsgo-reviewer
: API securitysecurity-scanner
: OpenAPI documentationapi-documenter