AsyncAPI
Auto-generate AsyncAPI 3.0 specifications for streaming endpoints
Forge automatically generates an AsyncAPI 3.0 specification for your WebSocket, Server-Sent Events (SSE), and WebTransport endpoints. The spec documents channels, operations, message schemas, and protocol bindings -- all derived from your route definitions.
What Is AsyncAPI?
AsyncAPI is to event-driven and streaming APIs what OpenAPI is to REST APIs. It provides a machine-readable specification that describes:
- Channels -- the paths clients connect to (e.g.,
/ws/chat,/events/notifications) - Operations -- what happens on each channel (send, receive)
- Messages -- the structure of data exchanged
- Protocol bindings -- protocol-specific configuration (WebSocket, HTTP/SSE)
Accessing the Spec
The generated AsyncAPI specification is served at:
GET /_/asyncapiEnabling AsyncAPI
Enable AsyncAPI generation by passing an AsyncAPIConfig to the router.
app := forge.New(
forge.WithAppRouterOptions(
forge.WithAsyncAPI(forge.AsyncAPIConfig{
Title: "My Streaming API",
Version: "1.0.0",
Description: "Real-time streaming endpoints",
Servers: map[string]forge.AsyncAPIServer{
"production": {
Host: "api.example.com",
Protocol: "wss",
Description: "Production WebSocket server",
},
"sse": {
Host: "api.example.com",
Protocol: "https",
Description: "Production SSE server",
},
},
}),
),
)Auto-Generation from Routes
When you define WebSocket or SSE routes, Forge automatically creates AsyncAPI channel and operation entries.
r := app.Router()
// WebSocket -- auto-generates a channel with send/receive operations
r.WebSocket("/ws/chat", chatHandler,
forge.WithSummary("Chat room"),
forge.WithTags("chat"),
)
// SSE -- auto-generates a channel with receive-only operation
r.SSE("/events/notifications", notificationHandler,
forge.WithSummary("Live notifications"),
forge.WithTags("notifications"),
)Message Schemas
Define the structure of messages exchanged on each channel.
WebSocket Messages
WebSocket channels support both send (client-to-server) and receive (server-to-client) messages.
type ChatMessage struct {
UserID string `json:"user_id"`
Content string `json:"content"`
Timestamp int64 `json:"timestamp"`
}
type ServerEvent struct {
Type string `json:"type"`
Payload any `json:"payload"`
}
r.WebSocket("/ws/chat", chatHandler,
forge.WithWebSocketMessages(
&ChatMessage{}, // send: client -> server
&ServerEvent{}, // receive: server -> client
),
forge.WithSummary("Real-time chat"),
)SSE Messages
SSE channels are receive-only. Define schemas per event name.
type OrderUpdate struct {
OrderID string `json:"order_id"`
Status string `json:"status"`
}
type StockPrice struct {
Symbol string `json:"symbol"`
Price float64 `json:"price"`
}
// Multiple event types on one channel
r.SSE("/events/trading", tradingHandler,
forge.WithSSEMessages(map[string]any{
"order_update": &OrderUpdate{},
"stock_price": &StockPrice{},
}),
)
// Single event type
r.SSE("/events/heartbeat", heartbeatHandler,
forge.WithSSEMessage("ping", &HeartbeatPayload{}),
)Message Examples
Add concrete examples to your message schemas.
r.WebSocket("/ws/chat", chatHandler,
forge.WithWebSocketMessages(&ChatMessage{}, &ServerEvent{}),
forge.WithMessageExample("send", "greeting", ChatMessage{
UserID: "user_123",
Content: "Hello everyone!",
Timestamp: 1700000000,
}),
forge.WithMessageExample("receive", "welcome", ServerEvent{
Type: "user_joined",
Payload: map[string]string{"user": "user_123"},
}),
)Channel Configuration
Customize channel names, descriptions, and summaries.
r.WebSocket("/ws/orders/:orderId", orderStreamHandler,
forge.WithAsyncAPIChannelName("order-updates"),
forge.WithAsyncAPIChannelDescription("Real-time updates for a specific order"),
forge.WithAsyncAPIChannelSummary("Order update stream"),
forge.WithAsyncAPIOperationID("streamOrderUpdates"),
forge.WithAsyncAPITags("orders", "streaming"),
)Protocol Bindings
Add protocol-specific configuration that appears in the AsyncAPI spec.
r.WebSocket("/ws/data", dataHandler,
forge.WithAsyncAPIBinding("ws", map[string]any{
"method": "GET",
"headers": map[string]any{
"type": "object",
"properties": map[string]any{
"Authorization": map[string]string{
"type": "string",
},
},
},
}),
)Correlation IDs
For request-reply patterns over WebSocket, define correlation IDs to match requests with responses.
r.WebSocket("/ws/rpc", rpcHandler,
forge.WithCorrelationID(
"$message.header#/correlationId",
"Unique identifier to correlate requests and responses",
),
)Message Headers
Define custom header schemas for messages.
type MessageHeaders struct {
CorrelationID string `header:"X-Correlation-ID"`
ContentType string `header:"Content-Type"`
}
r.WebSocket("/ws/events", eventsHandler,
forge.WithMessageHeaders(&MessageHeaders{}),
forge.WithMessageContentType("application/json"),
)Security
Add security requirements to streaming operations.
r.WebSocket("/ws/private", privateHandler,
forge.WithAsyncAPISecurity(map[string][]string{
"bearerAuth": {},
"apiKey": {"read", "write"},
}),
)Server Protocol Binding
Bind operations to specific servers defined in your AsyncAPI config.
// Only available on the "production" server
r.WebSocket("/ws/chat", chatHandler,
forge.WithServerProtocol("production"),
)External Documentation
Link to external docs for a channel or operation.
r.SSE("/events/webhooks", webhookHandler,
forge.WithAsyncAPIExternalDocs(
"https://docs.example.com/webhooks",
"Webhook integration guide",
),
)Excluding Routes from AsyncAPI
Prevent specific streaming routes from appearing in the AsyncAPI spec.
// Exclude a single route
r.WebSocket("/ws/internal/debug", debugHandler,
forge.WithAsyncAPIExclude(),
)
// Exclude from all schemas
r.WebSocket("/ws/internal/metrics", metricsHandler,
forge.WithSchemaExclude(),
)Complete Example
package main
import "github.com/xraph/forge"
type ChatMessage struct {
Room string `json:"room"`
UserID string `json:"user_id"`
Content string `json:"content"`
}
type SystemEvent struct {
Type string `json:"type"`
Data any `json:"data"`
}
type Notification struct {
ID string `json:"id"`
Title string `json:"title"`
Message string `json:"message"`
Read bool `json:"read"`
}
func main() {
app := forge.New(
forge.WithAppName("streaming-api"),
forge.WithAppRouterOptions(
forge.WithAsyncAPI(forge.AsyncAPIConfig{
Title: "Streaming API",
Version: "1.0.0",
Description: "Real-time communication channels",
}),
),
)
r := app.Router()
// WebSocket with full message schemas
r.WebSocket("/ws/chat/:room", chatHandler,
forge.WithSummary("Chat room"),
forge.WithTags("chat"),
forge.WithWebSocketMessages(&ChatMessage{}, &SystemEvent{}),
forge.WithAsyncAPIChannelDescription("Join a chat room for real-time messaging"),
forge.WithMessageExample("send", "hello", ChatMessage{
Room: "general",
UserID: "user_42",
Content: "Hello!",
}),
)
// SSE for notifications
r.SSE("/events/notifications", notifyHandler,
forge.WithSummary("Live notifications"),
forge.WithTags("notifications"),
forge.WithSSEMessage("notification", &Notification{}),
)
app.Run()
}Visit /_/asyncapi to see the generated specification. You can use tools like AsyncAPI Studio to visualize and explore the spec.
How is this guide?