Skip to content
wiki edited this page Sep 4, 2026 · 1 revision

rextension-swagger

Serves Swagger UI 5.18.2 from assets embedded in the binary — no CDN requests at runtime.

go get github.com/kryovyx/rextension-swagger
import (
	"github.com/kryovyx/rex"
	openapi "github.com/kryovyx/rextension-openapi"
	swagger "github.com/kryovyx/rextension-swagger"
)

app := rex.New(
	openapi.WithOpenAPI(nil),  // serves /openapi.json
	swagger.WithSwagger(nil),  // serves the UI at /apidoc
)

Open http://localhost:8080/apidoc/.

Routes it registers

Route
GET /apidoc 301/apidoc/
GET /apidoc/ the UI page
GET /apidoc/assets/*.css embedded stylesheets
GET /apidoc/assets/*.js embedded scripts
GET /apidoc/assets/themes/<name>.css custom themes, when registered

All from OnStart, before the route table is built.

They used to be registered from OnReady — by which point the listeners are bound and serving, so the registration added routes to a trie that in-flight requests were already reading, with no lock on either side. That is now refused by the framework rather than raced.

No dependency on rextension-openapi

The two modules do not import each other. The UI is pointed at a path:

swagger.WithSwagger(swagger.NewConfig(
	swagger.WithOpenAPIPath("/openapi.json"),
))

So it works against a document served by rextension-openapi, a static file, or an entirely different service — anything reachable from the browser.

Everything is embedded

swagger-ui-bundle.js, swagger-ui-standalone-preset.js, swagger-ui.css, a dark theme, and the required-roles plugin are compiled into the binary.

That matters for more than convenience: a UI loading scripts from a CDN is a third-party script with full access to the page a developer pastes bearer tokens into, and it stops working in an air-gapped environment.

The Required Authorization panel

If your routes declare roles, the UI shows them. Expanding an operation renders a panel with the scheme, the bearer format, the roles claim and the required roles as badges — built from x-required-roles and x-roles-claim in the document.

See Required Authorization.

Keep it off the public listener in production

The UI publishes your entire API surface and invites people to call it. It is a development and internal-documentation tool.

// Serve the document and the UI on an internal router.
openapi.WithOpenAPI(openapi.NewConfig(openapi.WithServeOnRouter("internal")))

Or register the extension only outside production:

opts := []rex.Option{openapi.WithOpenAPI(cfg)}
if env != "production" {
	opts = append(opts, swagger.WithSwagger(nil))
}
app := rex.New(opts...)

Pages

Clone this wiki locally