AsyncAPI Module
Module: github.com/redelay/go-framework/modules/asyncapi
The asyncapi module auto-generates an AsyncAPI 2.6 specification
from all registered module events and consumers, then serves it as YAML alongside an
interactive AsyncAPI Studio UI — the event equivalent of Scalar/Swagger UI for REST.
Quick start
import (
_ "github.com/redelay/go-framework/modules/asyncapi"
)
That's it. The module is auto-discovered at bootstrap. Your service will serve:
| Path | Description |
|---|---|
/asyncapi.json | AsyncAPI 2.6 spec, JSON (Content-Type: application/json) |
/asyncapi.yaml | AsyncAPI 2.6 spec, YAML (Content-Type: application/yaml) |
/asyncapi | AsyncAPI Studio — interactive event browser |
All three mount at the root, outside ROUTE_PREFIX, and are advertised in
/openapi.json at those absolute paths (via DynamicRoutesProvider with
AbsolutePath). The single spec handler content-negotiates by path suffix —
.json → JSON, anything else → YAML — so /asyncapi.json and /asyncapi.yaml
share one handler. Configure the paths with ASYNCAPI_SPEC_PATH,
ASYNCAPI_YAML_PATH, and ASYNCAPI_UI_PATH.
How the spec is generated
The module walks every registered module at startup (lazily, on first request) and collects:
- Events from
EventsProvider.Events()→ AsyncAPI channels (one channel per topic) - Consumers from
EventsProvider.Consumers()→ subscribe-side operations withgroupIdKafka bindings - Packet schemas from
ir.Event.Payload→components/schemas
Module 1: Events() → [user.created, user.deleted]
Module 2: Events() → [email.send]
Module 2: Consumers() → [user.created group=email-sender]
↓ asyncapi.Export()
channels:
user.created:
publish:
operationId: user.created
message: { $ref: '#/components/messages/UserCreated' }
email.send:
publish:
operationId: email.send
user.created:
subscribe:
operationId: consume_user.created
bindings:
kafka:
groupId: email-sender
If a module's ir.Event has no explicit Topic, the channel key defaults to entity_type.action.
Declaring events for the spec
Implement EventsProvider on your module. The Events() return value is both the runtime
contract (used by go-events for routing) and the source of truth for the AsyncAPI spec:
func (m *Module) Events() []*ir.Event {
return []*ir.Event{
{
Name: "order.created",
EntityType: "order",
Action: "created",
Topic: "order.created",
Description: "Fired when a new order is placed.",
Payload: &ir.Packet{
ID: "OrderCreatedPayload",
Name: "OrderCreatedPayload",
Fields: []*ir.Field{
{Name: "order_id", Type: ir.FieldTypeUUID, Required: true},
{Name: "total", Type: ir.FieldTypeFloat, Required: true},
{Name: "currency", Type: ir.FieldTypeString, Required: true},
},
},
},
}
}
func (m *Module) Consumers() []*modules.ConsumerRegistration {
return []*modules.ConsumerRegistration{
{
EventName: "payment.completed",
Topic: "payment.completed",
GroupID: "orders",
Handler: m.handlePaymentCompleted,
},
}
}
The Payload field is optional — if omitted, the message schema is left open. Adding it
produces a components/schemas entry and a $ref in the channel's message definition.
Example AsyncAPI output
For a service with auth and users modules, the generated spec looks like:
asyncapi: "2.6.0"
info:
title: My Service
version: "1.0.0"
channels:
auth.login:
description: Successful authentication
publish:
operationId: auth.login
summary: Successful authentication
message:
name: auth.login
contentType: application/json
users.user_created:
description: New user registered
publish:
operationId: users.user_created
message:
name: users.user_created
contentType: application/json
payload:
$ref: '#/components/schemas/UserCreatedPayload'
components:
schemas:
UserCreatedPayload:
type: object
properties:
user_id: { type: string }
email: { type: string }
required: [user_id, email]
AsyncAPI Studio UI
The /asyncapi endpoint serves a self-contained HTML page powered by
@asyncapi/react-component 3.x (standalone CDN build).
No Node.js or build step required.
The Studio renders:
- Sidebar — channel list with publish/subscribe badges
- Info panel — title, version, description
- Operations — one per event with message schema
- Schemas —
components/schemasbrowser
To disable the UI (spec-only mode):
ASYNCAPI_UI=disabled
Referencing the spec from FlowDSL
Services using FlowDSL can declare packet schemas by reference to the live AsyncAPI spec, ensuring event contracts stay in sync across services:
# In a .flowdsl.yaml file
packets:
- id: order_created_payload
$ref: "https://api.example.com/asyncapi.json#/components/schemas/OrderCreatedPayload"
See FlowDSL Integration for the full pattern.
Cross-language schema validation
Both Go and Python services can load the spec at startup and validate every incoming event payload against it:
Go (via go-events/schema):
// Automatically loaded when ASYNCAPI_URL env var is set.
// ASYNCAPI_URL=http://api-service/asyncapi.json
// Manual:
import "github.com/redelay/go-events/schema"
reg, err := schema.LoadFromURL("http://api-service/asyncapi.json")
err = reg.Validate("order.created", payloadBytes)
Python (coming soon):
# Set at startup — py-events loads and caches the schema.
ASYNCAPI_URL=http://api-service/asyncapi.json
Modspec integration
The /modules browser (modspec) links directly to
/asyncapi and /reference from its top navigation bar. Each module card shows an
Events tab and a Consumers tab alongside the diagram and YAML views.
Configuration
| Variable | Default | Description |
|---|---|---|
ASYNCAPI_UI | studio | UI provider (studio or disabled) |
ASYNCAPI_SPEC_PATH | /asyncapi.json | URL path for the YAML spec |
ASYNCAPI_UI_PATH | /asyncapi | URL path for the Studio UI |
APP_NAME | redelay | Service name in the spec info.title |
APP_VERSION | 1.0.0 | Version string in the spec info.version |
Programmatic spec generation
The underlying core/asyncapi package can generate or parse AsyncAPI specs directly,
independent of the HTTP module:
import "github.com/redelay/go-framework/core/asyncapi"
// Export IR to AsyncAPI YAML
err := asyncapi.Export(doc, writer)
// Import AsyncAPI YAML into IR
doc, err := asyncapi.Import(reader)
See Core IR & Compilation for full details on the compile pipeline.