Skip to content

AsyncAPI ​

chanx-go builds an AsyncAPI 3.0 document from the consumers you mounted: every route is a channel, every handler an operation, every message a schema. Because it comes from the code that serves traffic, it cannot drift, and typed clients generated from it always match the server.

Serving it ​

Each adapter has a MountDocs, serving three routes:

go
func Mount(mux *http.ServeMux, engine *chanx.Engine) {
	chanxhttp.MountDocs(mux, engine, "/asyncapi", Info) // /asyncapi, /asyncapi.json, /asyncapi.yaml
}
RouteServes
/asyncapian interactive page, the AsyncAPI React viewer
/asyncapi.jsonthe document as JSON, for client generators
/asyncapi.yamlthe same as YAML

Without MountDocs, asyncapi.HTMLHandler, JSONHandler and YAMLHandler are plain http.Handlers, and asyncapi.Generate returns the document itself.

What goes in it ​

From your codeIn the document
a route, /ws/chat/{room}a channel, with its address and path parameters
chanx.Name, chanx.Description, chanx.Tagsthe channel's name, description and tags
a handlera receive operation, named after the method in snake case
its return type, or Operations Repliesthe operation's reply
an event handler, Emits, Passthroughmessages the channel sends
a hosted topica channel of its own, with an x-topic entry for client generators
a message structa message and its payload schema, named after the type
Credentialssecurity schemes, operation security, WebSocket bindings

Request and reply messages carry a correlationId pointing at the frame's ref.

Metadata and servers ​

go
var Info = asyncapi.Info{
	Title:       "Chat API",
	Version:     "2.1.0",
	Description: "Rooms, presence and notifications.",
	Servers: map[string]any{
		"production": map[string]any{"host": "chat.example.com", "protocol": "wss"},
		"staging":    map[string]any{"host": "staging.chat.example.com", "protocol": "wss"},
	},
}
asyncapi.Info fieldDefault
Title, Version, DescriptionAsyncAPI Documentation, 1.0.0, none
ServerURL, ServerProtocolthe request's host, ws or wss from the request
Serversreplaces ServerURL and ServerProtocol with named AsyncAPI Server Objects
SecuritySchemesadded to those from Credentials; a name declared here wins
CamelizecamelCase names and properties, for a server with Config.Camelize

Writing it to a file ​

For CI, or to generate clients without running the server:

go
// MaybeWrite prints the document and exits with -asyncapi, for CI and client
// generators: go run . -asyncapi > asyncapi.json
func MaybeWrite(engine *chanx.Engine) {
	doc := flag.Bool("asyncapi", false, "print the AsyncAPI document and exit")
	flag.Parse()
	if *doc {
		if err := asyncapi.Write(os.Stdout, engine, Info); err != nil {
			os.Exit(1)
		}
		os.Exit(0)
	}
}
bash
go run . -asyncapi > asyncapi.json
npx @chanx-js/codegen asyncapi.json -o src/generated

A test comparing the output with a committed copy catches an API change in review.

The docs page and your security policy ​

The page loads @asyncapi/react-component 3.2.2 from unpkg, pinned with SRI hashes. To load nothing from a CDN, serve the two files yourself:

go
// SelfHosted serves the docs page's script and stylesheet from your own files,
// for a page that loads nothing from a CDN.
var SelfHosted = asyncapi.Info{
	Title:      "Chat API",
	DocsJSURL:  "/static/asyncapi/index.js",
	DocsCSSURL: "/static/asyncapi/default.min.css",
}

A custom URL drops the pinned hash; set DocsJSIntegrity and DocsCSSIntegrity to keep the check.

Under a Content-Security-Policy, the page needs the asset origin in script-src and style-src, 'unsafe-inline' for its inline script, and 'unsafe-eval', because the component compiles code at runtime. Apply that policy to the docs route only, or keep the docs out of production.

Next: Configuration.

MIT licensed