golang-gin-swagger
Scannednpx machina-cli add skill henriqueatila/golang-gin-best-practices/golang-gin-swagger --openclawgolang-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.yamlfrom 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:
@Routeruses{id}(OpenAPI style), not:id(Gin style)@Security BearerAuthmust match the name in@securityDefinitions.apikey BearerAuth- Use named structs in
@Success/@Failure— nevergin.H{}ormap[string]interface{}
Model Documentation
Architecture note: In clean architecture, domain entities should not carry
jsonorbindingtags. 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:
| Tag | Purpose | Example |
|---|---|---|
example:"..." | Sample value in Swagger UI | example:"jane@example.com" |
format:"..." | OpenAPI format | format:"uuid", format:"email", format:"date-time" |
enums:"a,b" | Allowed values | enums:"admin,user" |
swaggerignore:"true" | Exclude field from docs | Hide PasswordHash |
swaggertype:"string" | Override inferred type | For time.Time, sql.NullInt64 |
minimum: / maximum: | Numeric bounds | minimum:"1" maximum:"100" |
minLength: / maxLength: | String length bounds | minLength:"2" maxLength:"100" |
default:"..." | Default value | default:"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
| Gotcha | Fix |
|---|---|
swag CLI not found | Add $(go env GOPATH)/bin to $PATH |
| Docs not updating | Re-run swag init — no watch mode |
Blank import _ "myapp/docs" missing | Without 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 mismatch | Must match @securityDefinitions.apikey name exactly |
time.Time rendered as object | Add swaggertype:"string" format:"date-time" on field |
| Type not found during parsing | Add --parseInternal or --parseDependency flag |
map[string]interface{} in response | Replace with a named struct |
internal_ prefix on model names | Known 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 initto 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
- 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
- Step 2: Add general API annotations before main() in cmd/api/main.go, including title, version, description, host, basePath, and security definitions
- 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.