Skip to content
Back to skills

Go Api Reviewer

ASecurity

WHEN: Go API review with Gin/Echo/Fiber/Chi, router patterns, middleware, request handling WHAT: Router organization + Middleware patterns + Request validation + Error responses + OpenAPI WHEN NOT: General Go → go-reviewer, Rust API → rust-api-reviewer

  • 666 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 8, 2026
code-qualityrustgogitapisecuritydocumentation

Works with

  • api

Security analysis

A100/100

Scanned February 10, 2026

npx -y skills add majiayu000/claude-skill-registry --skill go-api-reviewer --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Go Api Reviewer?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Go Api Reviewer
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-go-api-reviewer/badge)](https://www.skillsdirectory.com/skills/majiayu000-go-api-reviewer)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: go-api-reviewer
description: |
  WHEN: Go API review with Gin/Echo/Fiber/Chi, router patterns, middleware, request handling
  WHAT: Router organization + Middleware patterns + Request validation + Error responses + OpenAPI
  WHEN NOT: General Go → go-reviewer, Rust API → rust-api-reviewer
---

# 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
| 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 |

```go
// 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 |

```go
// 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 |

```go
// 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 |

```go
// 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)
```go
// 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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.