← Files GophersARCHIVED FILE

skills/go-swagger/references/anti-patterns.md

4.04 KB · Oct 5, 2026 · 18:31 UTC

↓ Download file

# Swagger Anti-Patterns

## 1. Missing `docs` Import

```go
// main.go
import (
    _ "yourmod/docs"   // ← without this, the UI loads but the spec is empty
)
```

The generated `docs/docs.go` has an `init()` that registers the spec with swag's global state. Forget the blank import and the UI displays "Failed to load API definition" with no obvious cause.

## 2. Stale `docs/`

Annotations change in handlers; nobody runs `swag init`; the committed `docs/` describes last sprint's API. Clients break in subtle ways.

Solutions, in order of robustness:

- `go generate` on pre-commit hook.
- `make check-docs` in CI that diffs after regenerating.
- Don't commit `docs/` at all; regenerate at build time.

The CI diff is the most reliable — pre-commit hooks get skipped.

## 3. Primitive or Map as Body Type

```go
// @Param body body string true "Raw text"             // ← swag can't infer schema
// @Param body body map[string]any true "Arbitrary"    // ← same
```

OpenAPI requires a schema. Define a struct, even if it's a thin wrapper:

```go
type RawTextRequest struct {
    Content string `json:"content"`
}
// @Param body body api.RawTextRequest true "Text"
```

For genuinely free-form JSON, use `json.RawMessage` with `swaggertype:"object"`.

## 4. General Info in the Wrong File

```go
// internal/server/init.go  ← wrong place
// @title Orders API
```

`swag init` reads general info only from the file passed via `-g` (default `main.go`). Annotations elsewhere are silently ignored, and the spec ends up with no title or host.

```bash
swag init -g internal/server/init.go   # if you really must
```

## 5. No `@Security` on Protected Endpoints

The handler runs auth middleware; the annotation is missing. Swagger UI shows no padlock, the "Try it out" panel does not send the token, and consumers' generated clients omit auth altogether.

Apply `@Security` everywhere middleware applies. If everything is protected, declare a default scheme in main.

## 6. Exposing `/swagger/*` in Production

The UI is convenient — and a complete map of every endpoint, query parameter, and body schema. Behind a public load balancer, that's free reconnaissance for attackers.

Gate it:

```go
if cfg.Env != "production" {
    mux.Handle("/swagger/", httpSwagger.Handler(swaggerFiles.Handler))
}
```

Or behind admin auth:

```go
mux.Handle("/swagger/", adminAuth(httpSwagger.Handler(swaggerFiles.Handler)))
```

## 7. Multi-Word `@Tags` Without Quotes

```go
// @Tags order management      ← becomes tags: ["order", "management"]
```

```go
// @Tags "order management"
```

The UI then groups by the right name, and generated clients name the API class correctly.

## 8. Schema Drifts Between `validate` and `@Param`

```go
type CreateOrderRequest struct {
    Total int64 `json:"total" validate:"required,gt=0"`
}
// @Param body body api.CreateOrderRequest true "Order"
// Implementation: c.ShouldBindJSON → validate.Struct → 400 if Total <= 0
```

If the docs claim `minimum: 0` but the validator requires `> 0`, clients get cryptic 400s. Keep them in lockstep — use struct tags as the source of truth and document them in `@Description`:

```go
type CreateOrderRequest struct {
    // Total in cents. Must be strictly positive.
    Total int64 `json:"total" minimum:"1" example:"19999" validate:"required,gt=0"`
}
```

## 9. Generic Wrapper Spelled Wrong

```go
// @Success 200 {object} api.Response{Order}             ← won't parse
// @Success 200 {object} api.Response<api.Order>         ← old syntax, depends on version
// @Success 200 {object} api.Response[api.Order]         ← swag v2 generics
// @Success 200 {object} api.Response{data=api.Order}    ← composition syntax
```

Pick one project-wide. Mixing generates inconsistent schemas. `Response[T]` works only on swag v2+; for older versions, use the composition `{data=T}` form.

## 10. Hand-Editing `docs/docs.go`

Tempting when one field looks wrong, fatal when the next `swag init` wipes the change. Fix it at the annotation source. If swag genuinely cannot express what you need, file an issue and use `extensions:"x-..."` as a workaround.

SHA-256: dd593d779278c194fb1ad625e726378551462fdaec40c195cd5f071275eb0b73