Skip to main content
友田 陽大
Go & Echo in production
Go
Echo
OpenAPI
型安全
アーキテクチャ設計
ドキュメント

Echo's OpenAPI / Swagger: choosing between code-first (swag) and contract-first (oapi-codegen), and production operation

A guide to operating OpenAPI / Swagger documentation with Go Echo (v5). It explains, with real code and no sugar-coating, choosing between code-first (swag annotations) and contract-first (generating types/server from a schema with oapi-codegen), serving Swagger UI, verifying documentation consistency in CI, and the state of tool support on Echo v5 (the oapi-codegen stable version supports v4; v5 is experimental).

Published
Reading time
8 min read
Author
友田 陽大
Share

"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?

AspectCode-first (swag)Contract-first (oapi-codegen)
Source of truthGo code (annotations)OpenAPI spec (YAML/JSON)
Flowcode → annotations → spec generationspec → type/server generation → implementation
Ease of introLight (can bolt on)Somewhat heavy (write the spec first)
Type safetyAnnotations and implementation are separate (can drift)Spec = type. The implementation follows the contract
Multi-language clientsFrom the generated spec to each languageExcellent fit since the spec is central
Suited scenarioBolt docs onto an existing APIGreenfield, 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.

ToolEcho v5 support (as of June 2026)Recommended action
swag (annotation parsing)Since it parses comments, it worksUse as-is
swag's UI serving (echo-swagger)Mostly assumes v4Substitute with static serving
oapi-codegen (type generation)-generate types is spec→type, so it worksType 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):

  1. 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's e.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.
  2. Code-first (swag): annotations work on v5. UI via static serving. Optimal for bolting on documentation.
  3. 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

  1. Don't let docs rot by hand-writing. Auto-generate with code or spec as the source of truth.
  2. Bolt-on is swag (code-first), greenfield/contract-driven is oapi-codegen (contract-first).
  3. Swagger UI is most reliable via static serving (version-independent).
  4. 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.
  5. In the transition period, type generation + manual mount lowers tool dependence (ETC).
  6. Commit the artifacts and fail CI on a regeneration diff (mechanical consistency assurance).
  7. 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.

友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

I can take on the implementation from this article as an engagement

I build Go / Echo backends, from design to production

API design and migration to Echo v5, clean architecture (Controller/UseCase/Repository + DI), middleware and security, centralized error handling, graceful shutdown, and testing/CI. With experience building a clean-architecture backend in Go/Echo + google/wire, I implement APIs that don't fall over, are traceable, and are easy to change.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading