rgoussu@goussu: ~/library/go/protocols
~/library/go/protocols cat api-documentation.md

API documentation in Go

# OpenAPI, AsyncAPI, protobuf and GraphQL SDL tooling for net/http/chi, Gin, Echo and Fiber — oapi-codegen, swag, huma/fuego, buf and grpc-gateway — and Go's codegen-first take on the direction trade.

Conceptsaved 2026-08-09 #go#protocols#documentation#openapi#asyncapi#codegen#frameworks

Overview

Go has no runtime annotations, so the ecosystem answers the doc-from-code vs code-from-doc question with the tool it always reaches for: code generation, in both directions. Contract-first is unusually natural here — oapi-codegen emits server interfaces for every router in the landscape, and the gRPC world's buf toolchain is the same idea perfected. Code-first exists in two generations: comment-driven (swag) and the newer types-driven frameworks (huma, fuego) that derive the spec from handler signatures. This note maps each flavor onto net/http + chi, Gin, Echo and Fiber.

Key points

  • OpenAPI contract-first, the default move: oapi-codegen turns an OpenAPI 3 document into server interfaces + typed clients with dedicated targets for stdlib ServeMux, chi, Echo, Gin and Fiber — you implement the interface, drift is a compile error. ogen is the stricter, more opinionated alternative; goa goes further and makes a Go DSL the design source, generating code and spec from it.
  • OpenAPI code-first, comments generation: swag parses // @Summary-style handler comments into a (Swagger 2) spec, with serving shims per framework — gin-swagger, echo-swagger, fiber-swagger, plain http.Handler for stdlib. Fine for retrofitting; the comments rot like all comments.
  • OpenAPI code-first, types generation: huma (adapters for stdlib/chi/Gin/Echo/Fiber) and fuego (net/http-native) derive OpenAPI 3.1 from typed request/response structs and validate requests against it — the correct-by-construction middle path.
  • AsyncAPI is the thin spot: no dominant tool. asyncapi-codegen (lerenn) generates pub/sub bindings from an AsyncAPI 2/3 document (code-from-doc, NATS/Kafka); otherwise hand-author the spec, validate/render with the AsyncAPI CLI in CI, and keep payload contracts in a schema registry — watermill has no spec story of its own.
  • gRPC: proto is the source, buf is the pipeline: buf generate (protoc-gen-go + protoc-gen-go-grpc), buf lint and buf breaking as CI gates, Buf Schema Registry for rendered docs. grpc-gateway + protoc-gen-openapiv2 serves REST and its OpenAPI document from the same .proto — one contract, two protocols.
  • GraphQL is schema-first by consensus: gqlgen generates resolver scaffolding from SDL — the schema file is simultaneously the design artifact and the docs, introspection and playground come free.
  • Spec quality is a CI job: Spectral or vacuum for linting, oasdiff for breaking-change gates on OpenAPI; buf covers proto natively.

Details

Flavor × router/framework matrix

Flavor net/http (+ chi) Gin Echo Fiber
OpenAPI, contract-first oapi-codegen std-http/chi-server targets; ogen (stdlib-based) oapi-codegen gin-server oapi-codegen echo-server oapi-codegen fiber-server
OpenAPI, code-first (comments) swag + httpSwagger.Handler swag + gin-swagger swag + echo-swagger swag + fiber-swagger
OpenAPI, code-first (types) huma (chi/stdlib adapters), fuego (native) huma gin adapter huma echo adapter huma fiber adapter
AsyncAPI Framework-independent: asyncapi-codegen bindings or hand-authored spec + AsyncAPI CLI — attaches to the messaging clients, not the router ← same ← same ← same
gRPC docs buf pipeline; grpc-gateway mounts as a plain http.Handler next to your mux grpc-gateway behind gin.WrapH grpc-gateway via echo.WrapHandler Awkward: fasthttp ≠ net/http — run the gateway on a separate listener
GraphQL gqlgen serves a plain http.Handler gin.WrapH(srv) echo.WrapHandler(srv) fasthttp adaptor shim

The pattern in the bottom rows is the stdlib-correspondence story again: doc-serving tools ship as http.Handlers, so stdlib and chi mount them natively, Gin and Echo wrap them, and Fiber pays its fasthttp tax with adaptors or a second listener.

Choosing a direction in Go

The community's center of gravity: contract-first (oapi-codegen) when the API crosses a team or language boundary; types-first code-first (huma/fuego) for greenfield services that want the spec for free with validation included; swag only when retrofitting docs onto an existing service. If the service is internal and both ends are Go, skip the argument — gRPC with buf gives contract, codegen, docs and breaking-change gates in one toolchain, and grpc-gateway keeps a documented REST surface for everyone else.

Related