golang-gin-swagger
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.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.
More from cylixlee/cortex
eino-adk
Eino Agent Development Kit development skill. For building AI Agent applications including ChatModelAgent, workflows (Sequential/Parallel/Loop), multi-agent systems (Supervisor/PlanExecute), human-in-the-loop (interruption/approval). Use when users need to create Agents, use Runner for execution, manage tool calls, build multi-agent systems.
2pnpm
Node.js package manager with strict dependency resolution. Use when running pnpm specific commands, configuring workspaces, or managing dependencies with catalogs, patches, or overrides.
2vue-debug-guides
Vue 3 debugging and error handling for runtime errors, warnings, async failures, and SSR/hydration issues. Use when diagnosing or fixing Vue issues.
2frontend-design
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
2golang-gin-api
Build REST APIs with Go Gin framework. Covers routing, handler patterns, request binding/validation, middleware chains, error handling, security headers (OWASP), CORS, timeout middleware, and layered project structure. Use when creating Go web servers, REST endpoints, HTTP handlers, or working with the Gin framework. Also activate when the user mentions Gin routes, middleware, JSON responses, request parsing, or API structure in Go.
2design-pattern
Applies object-oriented design principles and design patterns to generate maintainable, extensible code. Use when generating code that requires proper architectural layering, SOLID principles, and appropriate design patterns to solve recurring software design problems.
2