📖 3 min read (~ 500 words).

Doc UIs & spec serving

server-middleware/docui ships ready-to-mount http.Handlers that serve the three popular OpenAPI documentation UIs and the spec document itself. Standard library only — no template engine, no asset bundler, no transitive OpenAPI dependency.

Two equivalent patterns

Each UI is exposed in two shapes; pick whichever fits your wiring style.

Direct handler wrap — SwaggerUI(next, opts...)

For when you already have an http.Handler you want to decorate.

api := myAPIHandler() // your application

handler := docui.SwaggerUI(api,
	docui.WithSpecURL("/swagger.json"),
	docui.WithUIBasePath("/"),
	docui.WithUIPath("docs"),
)

srv := &http.Server{
	Addr:              ":8080",
	Handler:           handler,
	ReadHeaderTimeout: readHeaderTimeout,
}

Full source: docs/examples/standalone/docui/main.go

Requests under the configured doc path render the UI; everything else falls through to next.

Middleware factory — UseSwaggerUI(opts...)

For composition with other middlewares (alice.New(...), your own chain, etc.):

mw := docui.UseSwaggerUI(
	docui.WithSpecURL("/swagger.json"),
	docui.WithUIPath("docs"),
)
mux.Handle("/", mw(api))

Full source: docs/examples/standalone/docui/main.go

Use* returns a func(http.Handler) http.Handler — the standard go-style middleware adapter.

Available UIs

UIDirectMiddleware factory
Swagger UIdocui.SwaggerUIdocui.UseSwaggerUI
Swagger UI OAuth2 cbdocui.SwaggerUIOAuth2Callbackdocui.UseSwaggerUIOAuth2Callback
RapiDocdocui.RapiDocdocui.UseRapiDoc
Redocdocui.Redocdocui.UseRedoc

The OAuth2 callback handler is the small static page Swagger UI redirects to after an OAuth2 authorization — mount it at the path you configure in your OAuth provider.

Common options

OptionPurposeDefault
WithUIBasePath(string)Base path the UI is served from. Slash is prepended if missing./
WithUIPath(string)Sub-path under the base path (final URL: {base}/{path}).docs
WithUITitle(string)HTML <title> of the rendered page.API documentation
WithUIAssetsURL(string)URL of the JS bundle for the UI.Redoc → https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js
RapiDoc → https://unpkg.com/rapidoc/dist/rapidoc-min.js
Swagger UI → https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js
WithUITemplate(tpl)Replace the bundled HTML template entirely (~string or ~[]byte).bundled minimal template
WithSpecURL(string)URL the UI fetches the spec from./swagger.json
WithSwaggerUIOptions(opts)Pass-through for Swagger-UI-specific knobs (OAuth2 client id, layout, …).zero value

WithUITemplate panics at request time if the supplied template fails to parse or execute — fail loud, not silent. Reference docs for the templates each UI accepts:

Serving the spec document — ServeSpec / UseSpec

The UIs only render — they do not host the spec document themselves. Use the Spec helpers for that:

handler := docui.ServeSpec(specBytes, api,
	docui.WithSpecPath("/swagger.json"),
)

Full source: docs/examples/standalone/docui/main.go

or as middleware:

mw := docui.UseSpec(specBytes,
	docui.WithSpecPath("/swagger.json"),
)
mux.Handle("/", mw(api))

Full source: docs/examples/standalone/docui/main.go

If you want the spec path the UIs use to stay in sync with the path the spec is served from:

uiOpts := []docui.Option{docui.WithSpecURL("/swagger.json")}
specOpt := docui.WithSpecPathFromOptions(uiOpts...)

handler := docui.SwaggerUI(
	docui.ServeSpec(specBytes, api, specOpt),
	uiOpts...,
)

Full source: docs/examples/standalone/docui/main.go

Putting it together

A complete net/http server with no OpenAPI runtime in the picture:

//go:embed openapi.yaml
var spec []byte

func puttingItTogether() {
	api := http.NewServeMux()
	api.HandleFunc("/v1/ping", func(w http.ResponseWriter, _ *http.Request) {
		_, _ = w.Write([]byte("pong"))
	})

	handler := docui.SwaggerUI(
		docui.ServeSpec(spec, api,
			docui.WithSpecPath("/openapi.yaml"),
		),
		docui.WithSpecURL("/openapi.yaml"),
		docui.WithUIPath("docs"),
		docui.WithUITitle("Demo API"),
	)

	srv := &http.Server{
		Addr:              ":8080",
		Handler:           handler,
		ReadHeaderTimeout: readHeaderTimeout,
	}
	log.Fatal(srv.ListenAndServe())
}

Full source: docs/examples/standalone/docui/main.go

Visit:

  • http://localhost:8080/docs — Swagger UI
  • http://localhost:8080/openapi.yaml — the spec document
  • http://localhost:8080/v1/ping — the application