Get the FREE Ultimate OpenClaw Setup Guide →

golang-gin-swagger

Scanned
npx machina-cli add skill henriqueatila/golang-gin-best-practices/golang-gin-swagger --openclaw
Files (1)
SKILL.md
11.5 KB

golang-gin-swagger — Swagger/OpenAPI Documentation

Generate and serve Swagger/OpenAPI documentation for Gin APIs using swaggo/swag. This skill covers the 80% you need daily: setup, handler annotations, model tags, Swagger UI, and doc generation.

When to Use

  • Adding Swagger/OpenAPI documentation to a Gin API
  • Documenting endpoints with request/response schemas
  • Serving Swagger UI for interactive API exploration
  • Generating swagger.json/swagger.yaml from Go annotations
  • Documenting JWT Bearer auth in OpenAPI spec
  • Setting up CI/CD to validate docs are up to date

Dependencies

# CLI tool (generates docs from annotations)
go install github.com/swaggo/swag/cmd/swag@latest

# Go module dependencies
go get -u github.com/swaggo/gin-swagger
go get -u github.com/swaggo/files

Ensure $(go env GOPATH)/bin is in your $PATH so the swag CLI is available.

General API Annotations

Place these directly before main() in cmd/api/main.go. Only one annotation block per project.

// @title           My API
// @version         1.0
// @description     Production-grade REST API built with Gin.

// @contact.name    API Support
// @contact.email   support@example.com

// @license.name    MIT
// @license.url     https://opensource.org/licenses/MIT

// @host            localhost:8080
// @BasePath        /api/v1
// @schemes         http https

// @securityDefinitions.apikey  BearerAuth
// @in                          header
// @name                        Authorization
// @description                 Enter: Bearer {token}
func main() { ... }

Serving Swagger UI

package main

import (
    "os"

    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    ginSwagger   "github.com/swaggo/gin-swagger"

    _ "myapp/docs" // CRITICAL: blank import registers the generated spec
)

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

    // Only expose Swagger UI outside production
    if os.Getenv("GIN_MODE") != "release" {
        r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
    }

    // ... register routes, start server
}

Access at: http://localhost:8080/swagger/index.html

Swagger UI options:

r.GET("/swagger/*any", ginSwagger.WrapHandler(
    swaggerFiles.Handler,
    ginSwagger.URL("/swagger/doc.json"),
    ginSwagger.DocExpansion("list"),           // "list"|"full"|"none"
    ginSwagger.DeepLinking(true),
    ginSwagger.DefaultModelsExpandDepth(1),    // -1 hides models section
    ginSwagger.PersistAuthorization(true),     // retains Bearer token across reloads
    ginSwagger.DefaultModelExpandDepth(1),    // expand depth for example section
    ginSwagger.DefaultModelRendering("example"), // "example"|"model"
))

Dynamic Host Configuration

Override spec values at runtime for multi-environment deploys:

import (
    "os"
    "myapp/docs"
)

func main() {
    docs.SwaggerInfo.Host     = os.Getenv("API_HOST") // e.g. "api.prod.example.com"
    docs.SwaggerInfo.Schemes  = []string{"https"}
    docs.SwaggerInfo.BasePath = "/api/v1"
    // ...
}

Handler Annotations

Annotate each handler to document the endpoint. Always start with a Go doc comment.

// CreateUser godoc
//
// @Summary      Create a new user
// @Description  Register a new user account
// @Tags         users
// @Accept       json
// @Produce      json
// @Param        request  body      domain.CreateUserRequest  true  "Create user payload"
// @Success      201      {object}  domain.User
// @Failure      400      {object}  domain.ErrorResponse
// @Failure      409      {object}  domain.ErrorResponse
// @Failure      500      {object}  domain.ErrorResponse
// @Router       /users [post]
func (h *UserHandler) Create(c *gin.Context) { ... }

// GetByID godoc
//
// @Summary      Get user by ID
// @Description  Retrieve a single user by UUID
// @Tags         users
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "User ID (UUID)"
// @Success      200  {object}  domain.User
// @Failure      400  {object}  domain.ErrorResponse
// @Failure      401  {object}  domain.ErrorResponse
// @Failure      404  {object}  domain.ErrorResponse
// @Failure      500  {object}  domain.ErrorResponse
// @Router       /users/{id} [get]
func (h *UserHandler) GetByID(c *gin.Context) { ... }

// List godoc
//
// @Summary      List users
// @Description  List users with pagination and optional role filter
// @Tags         users
// @Produce      json
// @Security     BearerAuth
// @Param        page   query     int     false  "Page number"      default(1) minimum(1)
// @Param        limit  query     int     false  "Items per page"   default(20) minimum(1) maximum(100)
// @Param        role   query     string  false  "Filter by role"   Enums(admin, user)
// @Success      200    {array}   domain.User
// @Failure      400    {object}  domain.ErrorResponse
// @Failure      401    {object}  domain.ErrorResponse
// @Failure      500    {object}  domain.ErrorResponse
// @Router       /users [get]
func (h *UserHandler) List(c *gin.Context) { ... }

Critical rules:

  • @Router uses {id} (OpenAPI style), not :id (Gin style)
  • @Security BearerAuth must match the name in @securityDefinitions.apikey BearerAuth
  • Use named structs in @Success/@Failure — never gin.H{} or map[string]interface{}

Model Documentation

Architecture note: In clean architecture, domain entities should not carry json or binding tags. Use separate request/response DTOs in the delivery layer. See golang-gin-clean-arch Golden Rule 4. The tags here are shown for Swagger annotation purposes — in a clean-arch project, apply them to DTO structs, not domain entities.

Add example, format, and constraint tags to struct fields for rich Swagger docs:

// internal/domain/user.go
package domain

import "time"

// User represents an authenticated system user.
type User struct {
    ID        string    `json:"id"         example:"550e8400-e29b-41d4-a716-446655440000" format:"uuid"`
    Name      string    `json:"name"       example:"Jane Doe"    minLength:"2" maxLength:"100"`
    Email     string    `json:"email"      example:"jane@example.com" format:"email"`
    Role      string    `json:"role"       example:"admin"       enums:"admin,user"`
    CreatedAt time.Time `json:"created_at" example:"2024-01-15T10:30:00Z" format:"date-time"`
    UpdatedAt time.Time `json:"updated_at" example:"2024-01-15T10:30:00Z" format:"date-time"`

    PasswordHash string `json:"-" swaggerignore:"true"`
}

// CreateUserRequest is the payload for POST /users.
type CreateUserRequest struct {
    Name     string `json:"name"     example:"Jane Doe"        binding:"required,min=2,max=100"`
    Email    string `json:"email"    example:"jane@example.com" binding:"required,email"`
    Password string `json:"password" example:"s3cur3P@ss!"      binding:"required,min=8"`
    Role     string `json:"role"     example:"user"             binding:"omitempty,oneof=admin user" enums:"admin,user"`
}

// ErrorResponse is the standard API error envelope.
type ErrorResponse struct {
    Error string `json:"error" example:"resource not found"`
}

Key struct tags for swag:

TagPurposeExample
example:"..."Sample value in Swagger UIexample:"jane@example.com"
format:"..."OpenAPI formatformat:"uuid", format:"email", format:"date-time"
enums:"a,b"Allowed valuesenums:"admin,user"
swaggerignore:"true"Exclude field from docsHide PasswordHash
swaggertype:"string"Override inferred typeFor time.Time, sql.NullInt64
minimum: / maximum:Numeric boundsminimum:"1" maximum:"100"
minLength: / maxLength:String length boundsminLength:"2" maxLength:"100"
default:"..."Default valuedefault:"20"

Generating Docs

# Standard cmd/ layout
swag init -g cmd/api/main.go -d ./,./internal/handler,./internal/domain

# Format annotations first (recommended)
swag fmt && swag init -g cmd/api/main.go

# Parse types from internal/ packages
swag init -g cmd/api/main.go --parseInternal

# Output: docs/docs.go, docs/swagger.json, docs/swagger.yaml

Commit the generated docs/ directory. Re-run swag init after every handler or model change.

Makefile Integration

.PHONY: docs docs-check

docs:
	swag fmt
	swag init -g cmd/api/main.go -d ./,./internal/... --exclude ./vendor

docs-check:
	swag init -g cmd/api/main.go -d ./,./internal/... --exclude ./vendor
	git diff --exit-code docs/

Common Gotchas

GotchaFix
swag CLI not foundAdd $(go env GOPATH)/bin to $PATH
Docs not updatingRe-run swag init — no watch mode
Blank import _ "myapp/docs" missingWithout it, spec is never registered — Swagger UI shows empty
@Router uses :id instead of {id}Use {id} in annotations (OpenAPI), :id in Gin routes
@Security name mismatchMust match @securityDefinitions.apikey name exactly
time.Time rendered as objectAdd swaggertype:"string" format:"date-time" on field
Type not found during parsingAdd --parseInternal or --parseDependency flag
map[string]interface{} in responseReplace with a named struct
internal_ prefix on model namesKnown bug with --parseInternal — use --useStructName

Excluding Swagger from Production Binary

Use build tags to strip Swagger UI from production builds entirely:

// file: swagger.go
//go:build swagger

package main

import (
    _ "myapp/docs"
    ginSwagger   "github.com/swaggo/gin-swagger"
    swaggerFiles "github.com/swaggo/files"
    "github.com/gin-gonic/gin"
)

func registerSwagger(r *gin.Engine) {
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
// file: swagger_noop.go
//go:build !swagger

package main

import "github.com/gin-gonic/gin"

func registerSwagger(r *gin.Engine) {} // no-op in production

Build with go build -tags swagger . for dev/staging, go build . for production.

Reference Files

Load these when you need deeper detail:

  • references/annotations.md — Complete annotation reference: all @Param types, file uploads, response headers, enum from constants, model renaming, grouped responses, multiple auth schemes
  • references/ci-cd.md — GitHub Actions workflow, PR validation, pre-commit hooks, OpenAPI 3.0 conversion, multiple swagger instances, swag init flags reference

Cross-Skill References

  • For handler patterns (ShouldBindJSON, route groups, error handling): see the golang-gin-api skill
  • For JWT middleware and @securityDefinitions.apikey BearerAuth: see the golang-gin-auth skill
  • For testing annotated handlers: see the golang-gin-testing skill
  • For adding swag init to Docker builds: see the golang-gin-deploy skill

Official Docs

If this skill doesn't cover your use case, consult the swag GitHub, gin-swagger GoDoc, or Swagger 2.0 spec.

Source

git clone https://github.com/henriqueatila/golang-gin-best-practices/blob/main/skills/golang-gin-swagger/SKILL.mdView on GitHub

Overview

Generate and serve Swagger/OpenAPI docs for Gin applications using swaggo/swag. This skill covers setup, handler annotations, model tagging, Swagger UI, security definitions, and CI/CD integration to keep API references up to date.

How This Skill Works

Install the swag CLI, annotate handlers and models with Swagger comments, and run swag to generate the docs. Import the generated docs package and wire up gin-swagger to serve the Swagger UI, with options for dynamic host and security definitions to support multi-environment deployments.

When to Use It

  • Adding Swagger/OpenAPI documentation to a Gin API
  • Documenting endpoints with request/response schemas
  • Serving Swagger UI for interactive API exploration
  • Generating swagger.json/swagger.yaml from Go annotations
  • Setting up CI/CD to validate docs are up to date

Quick Start

  1. Step 1: Install swag CLI and dependencies: go install github.com/swaggo/swag/cmd/swag@latest; go get -u github.com/swaggo/gin-swagger; go get -u github.com/swaggo/files
  2. Step 2: Add general API annotations before main() in cmd/api/main.go, including title, version, description, host, basePath, and security definitions
  3. Step 3: Run swag init to generate docs, import _ "myapp/docs" in your code, and register the Swagger UI route (e.g., r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler));) to access http://localhost:8080/swagger/index.html

Best Practices

  • Place annotation blocks directly before main() in cmd/api/main.go and maintain a single annotation block per project.
  • Import the generated docs package with a blank identifier to register the spec: _ "myapp/docs".
  • Run swag init in development and CI to regenerate docs whenever code or models change.
  • Expose Swagger UI only in non-production environments (e.g., GIN_MODE != 'release').
  • Use dynamic host/config (docs.SwaggerInfo) to adapt the OpenAPI spec across environments.

Example Use Cases

  • Documenting a full User API with Create, Read, Update, and Delete endpoints and JWT-required paths.
  • Adding a BearerAuth security scheme to OpenAPI and testing protected endpoints in Swagger UI.
  • Serving interactive docs in a staging environment for API exploration by QA.
  • Generating and versioning swagger.json in CI to detect drift when code changes.
  • Integrating swaggo/gin-swagger in a Go module project to produce and serve docs alongside the API.

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers