$npx -y skills add samber/cc-skills-golang --skill golang-swaggerGolang OpenAPI/Swagger documentation with swaggo/swag — annotation comments (@Summary, @Param, @Success, @Router, @Security), swag init code generation, framework integrations (gin, echo, fiber, chi, net/http), security definitions (Bearer/JWT, OAuth2, API key), and struct tags (
| 1 | **Persona:** You are a Go API documentation engineer. You treat docs as a contract — accurate, complete annotations prevent integration bugs and make the Swagger UI the source of truth for API consumers. |
| 2 | |
| 3 | **Modes:** |
| 4 | |
| 5 | - **Build** — adding Swagger to a new or existing Go project: set up the toolchain, annotate handlers, generate docs, wire the UI endpoint. |
| 6 | - **Audit** — reviewing existing swagger annotations for completeness, correctness, and security coverage. |
| 7 | |
| 8 | **Dependencies:** |
| 9 | |
| 10 | - swag: `go install github.com/swaggo/swag/cmd/swag@latest` |
| 11 | |
| 12 | ## Setup |
| 13 | |
| 14 | Three steps to get Swagger UI running: |
| 15 | |
| 16 | ```bash |
| 17 | swag init # generates docs/ with docs.go, swagger.json, swagger.yaml |
| 18 | swag init -g cmd/api/main.go # if general info is not in main.go |
| 19 | swag fmt # format annotation comments (like go fmt) |
| 20 | ``` |
| 21 | |
| 22 | Import the `docs` package to register the spec. Use a blank import when only wiring the UI; use a named import when you also need to override `docs.SwaggerInfo` at runtime: |
| 23 | |
| 24 | ```go |
| 25 | import _ "yourmodule/docs" // blank: registers spec, no identifier |
| 26 | import docs "yourmodule/docs" // named: use when overriding SwaggerInfo |
| 27 | ``` |
| 28 | |
| 29 | Wire the UI endpoint — pick your framework: |
| 30 | |
| 31 | ```go |
| 32 | // Gin |
| 33 | r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) |
| 34 | |
| 35 | // Echo |
| 36 | e.GET("/swagger/*", echoSwagger.WrapHandler) |
| 37 | |
| 38 | // Fiber |
| 39 | app.Get("/swagger/*", fiberSwagger.WrapHandler(swaggerFiles.Handler)) |
| 40 | |
| 41 | // net/http |
| 42 | mux.Handle("/swagger/", httpSwagger.Handler(swaggerFiles.Handler)) |
| 43 | |
| 44 | // Chi |
| 45 | r.Get("/swagger/*", httpSwagger.Handler(swaggerFiles.Handler)) |
| 46 | ``` |
| 47 | |
| 48 | Access the UI at `/swagger/index.html`. |
| 49 | |
| 50 | For dynamic host/basepath (multi-environment), use a named import and override before serving: |
| 51 | |
| 52 | ```go |
| 53 | import docs "yourmodule/docs" |
| 54 | |
| 55 | docs.SwaggerInfo.Host = os.Getenv("API_HOST") |
| 56 | docs.SwaggerInfo.BasePath = "/api/v1" |
| 57 | ``` |
| 58 | |
| 59 | [Full CLI reference](references/swag-cli.md) |
| 60 | |
| 61 | ## General API Info |
| 62 | |
| 63 | Place in `main.go` (or the file passed via `-g`). These annotations define the top-level spec: |
| 64 | |
| 65 | ```go |
| 66 | // @title My API |
| 67 | // @version 1.0 |
| 68 | // @description Short description of the API. |
| 69 | // @host localhost:8080 |
| 70 | // @BasePath /api/v1 |
| 71 | // @schemes http https |
| 72 | |
| 73 | // @contact.name API Support |
| 74 | // @contact.email support@example.com |
| 75 | // @license.name Apache 2.0 |
| 76 | |
| 77 | // @securityDefinitions.apikey Bearer |
| 78 | // @in header |
| 79 | // @name Authorization |
| 80 | // @description Type "Bearer" followed by a space and the JWT token. |
| 81 | ``` |
| 82 | |
| 83 | ## Operation Annotations |
| 84 | |
| 85 | Annotate each handler function. The standard doc comment (`// FuncName godoc`) must precede swag annotations — it anchors indentation for `swag fmt`. |
| 86 | |
| 87 | ```go |
| 88 | // ShowAccount godoc |
| 89 | // @Summary Get account by ID |
| 90 | // @Description Returns account details for the given ID. |
| 91 | // @Tags accounts |
| 92 | // @Accept json |
| 93 | // @Produce json |
| 94 | // @Param id path int true "Account ID" |
| 95 | // @Param filter query string false "Optional search filter" |
| 96 | // @Success 200 {object} model.Account |
| 97 | // @Success 204 "No content" |
| 98 | // @Failure 400 {object} api.ErrorResponse |
| 99 | // @Failure 404 {object} api.ErrorResponse |
| 100 | // @Router /accounts/{id} [get] |
| 101 | // @Security Bearer |
| 102 | func ShowAccount(c *gin.Context) {} |
| 103 | ``` |
| 104 | |
| 105 | **@Param** format: `@Param <name> <in> <type> <required> "<description>" [attributes]` |
| 106 | |
| 107 | | `<in>` | Usage | |
| 108 | | ---------- | ------------------------------------ | |
| 109 | | `path` | URL path segment (`/users/{id}`) | |
| 110 | | `query` | URL query string (`?filter=x`) | |
| 111 | | `body` | Request body — type must be a struct | |
| 112 | | `header` | HTTP header | |
| 113 | | `formData` |