"The API docs are out of sync with the implementation" — this is a chronic disease of every team that provides an API. Hand-written Markdown always rots, and the frontend or client companies implement believing the old docs, causing incidents. The solution isn't willpower but automation that bundles code, spec, and docs into a single source of truth.
This article is the API-documentation installment of the Go Echo production-operations guide. It designs choosing between the two approaches to operating OpenAPI on Echo v5 — code-first (swag) and contract-first (oapi-codegen) — and ensuring consistency in production. And it tells you, with no sugar-coating, the state of tool support given that Echo v5 is new.
Rules for this article: Echo's API is based on the official documentation (v5, as of June 2026). Important: since Echo v5 is right after release (June 2026), third-party support for
oapi-codegen, Swagger UI serving middleware, etc., is still catching up. After stating that reality, this article shows ways of writing that work now on v5. Always confirm each tool's latest support before introduction.
0. The two approaches: which "source of truth" do you choose?
| Aspect | Code-first (swag) | Contract-first (oapi-codegen) |
|---|---|---|
| Source of truth | Go code (annotations) | OpenAPI spec (YAML/JSON) |
| Flow | code → annotations → spec generation | spec → type/server generation → implementation |
| Ease of intro | Light (can bolt on) | Somewhat heavy (write the spec first) |
| Type safety | Annotations and implementation are separate (can drift) | Spec = type. The implementation follows the contract |
| Multi-language clients | From the generated spec to each language | Excellent fit since the spec is central |
| Suited scenario | Bolt docs onto an existing API | Greenfield, multiple teams, contract-driven |
Decision guide: if you want to quickly add docs to an existing Echo API, swag. If you're greenfield and want to fix the contract first with the frontend and external clients, oapi-codegen (contract-first). If you aim for end-to-end type safety including the frontend (Next.js, etc.), contract-first is the main play (Next.js × Go × OpenAPI in practice).
1. Code-first: generate OpenAPI from annotations with swag
swag parses handlers' comment annotations to generate an OpenAPI spec (swagger.json / swagger.yaml). Since annotation parsing just reads Go code comments, it doesn't depend on the framework version (it works on v5 too).
// CreateUser godoc
// @Summary ユーザーを作成
// @Description メールとパスワードで新規ユーザーを登録する
// @Tags users
// @Accept json
// @Produce json
// @Param request body CreateUserDTO true "作成するユーザー"
// @Success 201 {object} UserResponse
// @Failure 400 {object} ErrorResponse
// @Failure 422 {object} ErrorResponse
// @Router /api/v1/users [post]
func (h *UserHandler) Create(c *echo.Context) error {
// ... 実装
}
Generation is one command. You can build it into CI.
# 注釈から docs/swagger.json / swagger.yaml を生成
swag init -g cmd/server/main.go -o ./docs
The DTO written in annotations can reference the same struct as your binding & validation. The caveat is that, since annotations and implementation are separate, changing the type in @Param doesn't make the implementation follow (it can drift). How to detect this in CI is covered in Section 5.
2. Serving Swagger UI: on v5, "static serving" is the most reliable
Make the generated spec viewable from the browser with Swagger UI. There's dedicated middleware like echo-swagger, but those depend on Echo's version (at this point right after v5, most assume v4). The version-independent, reliable method is to serve the spec JSON and Swagger UI as ordinary static routes.
// 生成した OpenAPI 仕様を普通のルートで配る(バージョン非依存)
e.GET("/openapi.json", func(c *echo.Context) error {
return c.File("docs/swagger.json")
})
// Swagger UI / Redoc / Scalar 等の静的アセットを配信し、上の /openapi.json を指す
e.Static("/docs", "third_party/swagger-ui") // index.html が /openapi.json を読む
With this method, you can mount any viewer — Swagger UI, Redoc, or Scalar — regardless of Echo's version. The production practice is to place doc serving inside authentication or narrow the public scope (don't expose an internal API's spec defenselessly).
3. Contract-first: generate types from the spec with oapi-codegen
In contract-first, you write the OpenAPI spec first and generate Go types (and the server's foundation) from it. The spec becomes the single source of truth, and the implementation is forced to follow the contract.
# openapi.yaml(真実源)
paths:
/users:
post:
operationId: createUser
requestBody:
required: true
content:
application/json:
schema: { $ref: '#/components/schemas/CreateUserRequest' }
responses:
'201':
content:
application/json:
schema: { $ref: '#/components/schemas/User' }
components:
schemas:
CreateUserRequest:
type: object
required: [name, email]
properties:
name: { type: string, minLength: 2, maxLength: 50 }
email: { type: string, format: email }
Generate types from this and you don't have to hand-write the request/response structs, and spec changes propagate to the implementation as type errors.
# 仕様から Go の型を生成(型だけなら最も移植性が高い)
oapi-codegen -generate types -package api -o api/types.gen.go openapi.yaml
Use the generated types in the Echo handler's Bind/Validate. "Change minLength:2 in the spec, and the generated type and validation follow" — this is contract-first type safety.
4. The honest current state: Echo v5 and the state of tool support
Many tech articles muddy this, so I'll write it with no sugar-coating. Echo v5 was just released in June 2026, and since the handler signature changed to func(c *echo.Context) error, code-generation tools and middleware are still catching up.
| Tool | Echo v5 support (as of June 2026) | Recommended action |
|---|---|---|
| swag (annotation parsing) | Since it parses comments, it works | Use as-is |
| swag's UI serving (echo-swagger) | Mostly assumes v4 | Substitute with static serving |
| oapi-codegen (type generation) | -generate types is spec→type, so it works | Type generation usable |
| oapi-codegen (Echo server generation) | The stable version (v2) assumes Echo v4. v5 support is at the experimental (V3) stage (Go 1.25 required) | Type generation + manual mount, or carefully evaluate the experimental version |
A realistic strategy (v5, as of June 2026):
- Contract-first + type generation only: generate types only with
oapi-codegen -generate types, and write the routing and handler wiring yourself (wire it by hand to Echo v5'se.POST(...)). You get the contract's benefit (type safety) right now without waiting for the generated server's v5 support. This is the most solid. - Code-first (swag): annotations work on v5. UI via static serving. Optimal for bolting on documentation.
- If you need the generated server: wait until the stable version supports v5, or evaluate the experimental version outside production.
This "a new framework's core leads, and peripheral tools follow" is a universal phenomenon in every tech migration. That's exactly why a design that doesn't over-depend on tools (use type generation but don't be bound to server generation) lowers transition-period risk. This is also a practice of ETC (ease of change).
5. CI: ensure consistency mechanically
The value of OpenAPI operation arises only when the docs always match the implementation. Enforce this not by discipline but mechanically with CI.
# .github/workflows/ci.yml(抜粋)
- name: Regenerate & verify OpenAPI is in sync
run: |
swag init -g cmd/server/main.go -o ./docs # コードファーストなら再生成
# oapi-codegen の場合:oapi-codegen ... -o api/types.gen.go openapi.yaml
git diff --exit-code docs/ api/ # 差分が出たら CI を落とす
Commit the generated artifacts to the repository, and make CI "fail if regeneration produces a diff." This guarantees the spec, artifacts, and implementation always match at one point in Git. The instant someone forgets to update an annotation or the spec, CI fails, so the docs don't rot. Furthermore, adding breaking-change detection of the OpenAPI spec (oasdiff, etc.) to CI lets you stop backward-incompatible changes at the PR.
This philosophy of "ensure consistency with CI, not human attention" is exactly the same as quality gates for AI-driven development and end-to-end type safety (commit the artifacts and fail on regeneration diff).
Summary: the 7 principles of operating Echo's OpenAPI in production
- Don't let docs rot by hand-writing. Auto-generate with code or spec as the source of truth.
- Bolt-on is swag (code-first), greenfield/contract-driven is oapi-codegen (contract-first).
- Swagger UI is most reliable via static serving (version-independent).
- The honest v5 state: swag annotations and oapi-codegen's type generation work. Echo server generation's stable version assumes v4, and v5 is at the experimental stage.
- In the transition period, type generation + manual mount lowers tool dependence (ETC).
- Commit the artifacts and fail CI on a regeneration diff (mechanical consistency assurance).
- Add breaking-change detection to CI to protect the contract's backward compatibility.
API documentation isn't "something you write" but "something where you build a mechanism that doesn't deviate from the implementation." On a new platform like Echo v5, the realistic answer of discerning the tools' support state and taking only the benefit of type safety ahead of time pays off. For the full picture, the Go Echo production-operations guide; for observability, OpenTelemetry integration.