golang-gin-swagger

Generate Swagger/OpenAPI documentation for Go Gin APIs using swaggo/swag. Covers full CRUD + auth endpoint annotations, model documentation, Swagger UI setup, security definitions, and CI/CD integration. Use when adding API docs, Swagger UI, OpenAPI spec, endpoint documentation, or auto-generated API reference to a Gin application.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "golang-gin-swagger" with this command: npx skills add henriqueatila/golang-gin-best-practices/henriqueatila-golang-gin-best-practices-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.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 Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

golang-gin-api

No summary provided by upstream source.

Repository SourceNeeds Review
General

golang-gin-database

No summary provided by upstream source.

Repository SourceNeeds Review
General

golang-gin-psql-dba

No summary provided by upstream source.

Repository SourceNeeds Review
General

golang-gin-testing

No summary provided by upstream source.

Repository SourceNeeds Review