Skip to content

Go library for parsing, validating, and generating code from AsyncAPI 3.0.0 documents

License

Notifications You must be signed in to change notification settings

benelser/go-asyncapi

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-asyncapi

A Go library for parsing, validating, and generating code from AsyncAPI 3.0.0 documents.

Features

  • Full AsyncAPI 3.0.0 Support: Parse YAML and JSON documents
  • Type-Safe API: Strongly-typed Go structs with the *Ref pattern for references
  • Validation: JSON Schema validation + semantic validation rules
  • Reference Resolution: Local and external $ref resolution with cycle detection
  • Traits Merge: RFC 7386 JSON Merge Patch for operation and message traits
  • Code Generation: Generate Go types, handlers, and clients from AsyncAPI specs
  • Protocol Bindings: Support for HTTP, WebSocket, Kafka, AMQP, MQTT, NATS
  • CLI Tool: Command-line interface for validation and code generation

Installation

go get github.com/belser/go-asyncapi

Quick Start

Parsing a Document

package main

import (
    "fmt"
    "log"

    asyncapi "github.com/belser/go-asyncapi"
)

func main() {
    // Load from file
    doc, err := asyncapi.LoadFromFile("api.yaml")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("API: %s v%s\n", doc.Info.Title, doc.Info.Version)

    // Iterate over channels
    for name, ch := range doc.Channels {
        if ch.Value != nil {
            fmt.Printf("Channel: %s -> %s\n", name, *ch.Value.Address)
        }
    }

    // Iterate over operations
    for name, op := range doc.Operations {
        if op.Value != nil {
            fmt.Printf("Operation: %s (%s)\n", name, op.Value.Action)
        }
    }
}

Validating a Document

doc, err := asyncapi.LoadFromFile("api.yaml")
if err != nil {
    log.Fatal(err)
}

// Full validation (JSON Schema + semantic rules)
if err := doc.Validate(); err != nil {
    log.Fatal("Validation failed:", err)
}

// Or get detailed results
result := doc.ValidateAll()
for _, e := range result.Errors {
    fmt.Printf("%s: %s\n", e.Path, e.Message)
}

Resolving References

doc, err := asyncapi.LoadFromFile("api.yaml")
if err != nil {
    log.Fatal(err)
}

// Resolve all $ref pointers
if err := doc.ResolveRefs(); err != nil {
    log.Fatal(err)
}

// Now all references are populated
op := doc.Operations["userSignup"]
if op.Value != nil && op.Value.Channel.Value != nil {
    fmt.Printf("Channel address: %s\n", *op.Value.Channel.Value.Address)
}

Merging Traits

doc, err := asyncapi.LoadFromFile("api.yaml")
if err != nil {
    log.Fatal(err)
}

// Merge operation and message traits
if err := doc.MergeTraits(); err != nil {
    log.Fatal(err)
}

Code Generation

package main

import (
    "log"

    asyncapi "github.com/belser/go-asyncapi"
    "github.com/belser/go-asyncapi/gen"
)

func main() {
    doc, err := asyncapi.LoadFromFile("api.yaml")
    if err != nil {
        log.Fatal(err)
    }

    // Resolve refs and merge traits first
    doc.ResolveRefs()
    doc.MergeTraits()

    // Generate code
    g := gen.New().
        WithPackage("myapi").
        WithOutput("./generated")

    if err := g.Generate(doc); err != nil {
        log.Fatal(err)
    }
}

CLI Usage

Build the CLI

go build -o asyncapi ./cmd/asyncapi

Validate a Document

asyncapi validate api.yaml
# ✓ api.yaml is valid

# Schema-only validation
asyncapi validate --schema-only api.yaml

# Semantic-only validation
asyncapi validate --semantics-only api.yaml

Parse and Dump

# Output as YAML
asyncapi parse api.yaml

# Output as JSON
asyncapi parse --format json api.yaml

# Resolve references first
asyncapi parse --resolve api.yaml

Generate Code

# Generate to a directory
asyncapi generate --output ./gen --package api api.yaml

# Generate types only
asyncapi generate --output ./gen --types-only api.yaml

Walking the Document (For Code Generation)

The library provides multiple ways to traverse the document for code generation:

Using the Visitor Pattern

doc, _ := asyncapi.LoadFromFile("api.yaml")
doc.ResolveRefs()

doc.Walk(&asyncapi.Visitor{
    VisitSchema: func(name string, schema *asyncapi.Schema) bool {
        fmt.Printf("Schema: %s\n", name)
        if schema.IsObject() {
            for propName, prop := range schema.SchemaProperties() {
                fmt.Printf("  - %s: %v\n", propName, prop.Type)
            }
        }
        return true // continue walking
    },
    VisitMessage: func(name string, msg *asyncapi.Message) bool {
        fmt.Printf("Message: %s\n", name)
        if payload := msg.PayloadSchema(); payload != nil {
            // Generate message type from payload schema
        }
        return true
    },
    VisitOperation: func(name string, op *asyncapi.Operation) bool {
        fmt.Printf("Operation: %s (%s)\n", name, op.Action)
        return true
    },
})

Using Helper Methods

doc, _ := asyncapi.LoadFromFile("api.yaml")
doc.ResolveRefs()

// Get all schemas for type generation
for name, schema := range doc.AllSchemas() {
    generateType(name, schema)
}

// Get all messages
for _, msg := range doc.AllMessages() {
    if payload := msg.PayloadSchema(); payload != nil {
        generateMessageType(msg.Name, payload)
    }
}

// Group operations by action
publishers, subscribers := doc.OperationsByAction()
for _, op := range publishers {
    generatePublisher(op)
}
for _, op := range subscribers {
    generateSubscriber(op)
}

// Get operations for a specific channel
ops := doc.ChannelOperations("userSignedup")

Schema Type Helpers

schema := doc.AllSchemas()["User"]

if schema.IsObject() {
    for name, prop := range schema.SchemaProperties() {
        goType := schemaToGoType(prop)
        fmt.Printf("%s %s\n", name, goType)
    }
}

if schema.IsArray() {
    itemSchema := schema.ItemSchema()
    // Generate slice type
}

Type System

The library uses the *Ref pattern (inspired by kin-openapi) for handling JSON References:

// ServerRef can be either a $ref or an inline Server
type ServerRef struct {
    Ref   string  // The $ref URI (e.g., "#/components/servers/production")
    Value *Server // The resolved/inline value
}

When parsing, inline values populate Value directly. References set Ref and leave Value nil until ResolveRefs() is called.

Semantic Validation Rules

Beyond JSON Schema validation, the library enforces these semantic rules:

  • Unique operation IDs across all operations
  • Channel {param} placeholders must have corresponding parameter definitions
  • Server {var} placeholders must have corresponding variable definitions
  • No duplicate tag names within the same object
  • Valid runtime expression syntax ($message.header#/...)

Protocol Bindings

The library supports bindings for:

  • HTTP
  • WebSocket
  • Kafka
  • AMQP
  • MQTT
  • NATS
if op.Value.Bindings != nil && op.Value.Bindings.Value != nil {
    if kafka := op.Value.Bindings.Value.Kafka; kafka != nil {
        fmt.Printf("Kafka binding version: %s\n", kafka.BindingVersion)
    }
}

Testing

# Run all tests
go test ./...

# Run with verbose output
go test -v ./...

# Run fuzz tests
go test -fuzz=FuzzLoadFromData -fuzztime=30s .

Project Structure

go-asyncapi/
├── asyncapi.go        # Package documentation
├── document.go        # Document type
├── info.go            # Info, Contact, License
├── server.go          # Server, ServerVariable
├── channel.go         # Channel, Parameter
├── operation.go       # Operation, OperationTrait
├── message.go         # Message, MessageTrait
├── schema.go          # JSON Schema types
├── security.go        # SecurityScheme, OAuthFlows
├── bindings.go        # Protocol bindings
├── components.go      # Components object
├── reference.go       # *Ref types
├── resolver.go        # Reference resolution
├── validate.go        # Validation logic
├── loader.go          # File/URL loading
├── traits.go          # Traits merge
├── runtime_expr.go    # Runtime expression parser
├── errors.go          # Error types
├── gen/               # Code generator
│   ├── generator.go
│   └── types.go
└── cmd/asyncapi/      # CLI tool
    └── main.go

License

MIT

About

Go library for parsing, validating, and generating code from AsyncAPI 3.0.0 documents

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages