go-asyncapi
Loads AsyncAPI 3 documents in Go. One call, asyncapi.Load(path), gives you the servers, channels, operations and messages of a spec as plain structs, with $refs already followed and traits merged.
For install, the full type reference and schema walking, see references/setup.md.
Security & safety
- The library only reads files: the spec you pass to
Loadand the local files it references through relative$refs. It writes nothing and opens no network connection. A$refto anhttp(s)://URL fails withcommerr.ErrInvalidArgumentinstead of being fetched. - It compiles into your own binary and has no runtime surface of its own.
When to use
- Go code needs the contents of an AsyncAPI 3 spec: list channels and their messages, read server URLs, find which messages an operation sends or receives.
- You are writing a generator, linter, doc tool or test helper on top of AsyncAPI 3 and want
$refresolution and trait merging done for you. - The project already imports
github.com/psyb0t/go-asyncapi.
When NOT to use
- The spec is AsyncAPI 2.x.
Loadrejects it withcommerr.ErrInvalidValue; only 3.x documents load. - You want generated types and a client rather than a parsed document. Use gen-asyncapi, which is built on this library.
- You need full JSON Schema validation of payloads. go-asyncapi hands schemas back as
*asyncapi.Objecttrees and does not validate messages against them.
Usage
import (
"fmt"
asyncapi "github.com/psyb0t/go-asyncapi"
)
document, err := asyncapi.Load("asyncapi.yaml")
if err != nil {
return err // errors.Is(err, commerr.ErrNotFound) for a dangling $ref
}
for _, server := range document.Servers {
fmt.Println(server.Name, server.URL()) // variables filled with their defaults
}
for _, operation := range document.Operations {
if operation.Action != asyncapi.ActionReceive {
continue
}
for _, message := range operation.Messages {
fmt.Println(operation.Channel.Address, message.Name, message.ContentType)
}
}
Walking a payload schema:
message := document.Messages[0]
schema, at, err := document.Resolver.Deref(message.Payload, message.PayloadAt)
if err != nil {
return err
}
object, ok := schema.(*asyncapi.Object)
if !ok {
return nil // a boolean schema such as `payload: true`
}
properties := object.Object("properties")
if properties == nil {
return nil // not an object schema: oneOf, allOf, a primitive and so on
}
for _, name := range properties.Keys {
property, propertyAt, err := document.Resolver.Deref(
properties.Values[name],
at.Child("properties").Child(name),
)
if err != nil {
return err
}
_ = property // *asyncapi.Object, or a bool schema
_ = propertyAt // pass on to resolve $refs nested deeper
}
Always carry the Location that Deref returns and build child locations with Child. A $ref inside a schema resolves against the file that schema came from, and the location is how the resolver knows which file that is.
