Another service needs to know which messages your application sends, where they
travel, and what a delivery contains. arkoren.dev/core/asyncapi combines your
typed messaging definitions with explicit documentation metadata to export
AsyncAPI 3.1.0 JSON. Generating that document requires no broker connection.
Use it for broadcast topics and queued jobs. It neither publishes messages nor registers consumers. The synchronous event bus is a different mechanism; OpenAPI describes HTTP contracts.
Contract Model
A message describes one complete wire payload. A channel names the
destination where messages travel, such as a topic or queue. Several message
types can share a channel. An operation selects the messages your application
sends or receives on that channel: Send means out of this application, and
Receive means into it, never the broker’s perspective.
The typed helpers connect these declarations to application-owned definitions.
JSONTopic takes a broadcast.Topic[T]; JSONJob takes a queue.Job[T]. Each
helper uses the definition’s logical name to describe an Arkoren JSON envelope
and stores the decoded payload schema separately. You still supply the physical
destination and the application’s operations. Nothing discovers producers,
samples traffic, or reads driver configuration.
These are descriptions, not runtime validation or delivery policies. A document does not establish durable delivery, retries, ordering, or authorization. Keep those responsibilities in your messaging drivers, handlers, and infrastructure.
Shared Definitions
Suppose your reporting service announces completed reports. Keep the topic in
your application’s shared contract package so the runtime publisher and the
document can use the same identity. With broadcast imported from
arkoren.dev/core/service/broadcast, define its small logical payload:
type ReportReady struct {
ReportID string `json:"report_id"`
}
var Ready = broadcast.NewTopic[ReportReady]("report.ready")
Ready identifies the fact; it does not identify a broker endpoint. Use
JSONTopic only if your transport JSON-encodes broadcast.Message. A different
wire representation needs an explicit message schema.
Document Generation
Import arkoren.dev/core/asyncapi and arkoren.dev/foundation/validate. In the
same application package, describe the physical destination and sending role:
var Document = asyncapi.New("Reporting events", "1.0.0", func(definition *asyncapi.Builder) {
definition.Channel("reports", "production.reports", func(channel *asyncapi.ChannelBuilder) {
channel.JSONTopic("ready", Ready, validate.Infer[ReportReady]())
})
definition.Send("announce-report", "reports", func(action *asyncapi.OperationBuilder) {
action.Messages("ready")
})
})
validate.Infer describes the Go payload’s JSON shape. The wire message is a JSON
envelope whose payload field contains base64-encoded JSON, not an inline
ReportReady object. Messages and Schemas
explains both layers and the supported schema profile.
This portable document omits broker endpoints. If you add server declarations, every channel must explicitly select at least one server. Every operation must select at least one message from its channel, whether or not servers are declared.
Generate the bytes in a test, build command, or other application code:
data, err := Document.JSON()
On success, data contains deterministic JSON with asyncapi: "3.1.0", a
reports channel, and an announce-report send operation. 1.0.0 is your
contract’s version, not the AsyncAPI specification version. Handle err before
using the bytes; Serving Documentation covers file exports and HTTP
access. Focused snippets in these guides keep errors visible but omit routine
propagation; complete application code must handle them.
Stable Names
Choose document IDs independently of runtime names. This example separates four values with different purposes:
| Value | Example | Purpose |
|---|---|---|
| Channel ID | reports |
Local reference used by operations. |
| Channel address | production.reports |
Literal destination used by the deployed transport. |
| Topic name | report.ready |
Logical discriminator carried in the envelope. |
| Message ID | ready |
Local selection within the channel. |
Document IDs accept nonempty ASCII letters, digits, dots, underscores, and
hyphens. Duplicate IDs are errors, not replacements. Keep channel/message ID
pairs unambiguous: typed logical schema components use <channel>.<message>,
so pairs such as reports.v1 / ready and reports / v1.ready collide.
The exporter does not apply a driver’s destination prefixes or routing rules.
Supply the deployed physical address yourself. An empty address exports as
null, meaning unknown; it does not choose a default destination. Address
templates containing braces are unsupported.
Early Checks
New runs supplied construction callbacks synchronously, once in order, and
captures schemas and examples immediately. It retains declaration errors rather
than returning them. JSON() checks the completed graph, including forward
references, and caches either the export or its error. Run an export before
deployment so an invalid declaration does not first fail on an HTTP request.
The resulting document is immutable and has no startup or cleanup work. Construct a new document when the contract changes; changing a retained builder or an exported byte slice does not update it. The exact snapshot and failure behavior also applies when you serve the document.
For all declarations and options, see the
asyncapi API reference.