AsyncAPI Integration
Steel automatically generates comprehensive AsyncAPI 2.6 documentation from your WebSocket and Server-Sent Events handlers. Similar to OpenAPI for REST APIs, AsyncAPI provides standardized documentation for asynchronous APIs without requiring manual YAML files or annotations.
Quick Setup
Enable AsyncAPI documentation alongside your WebSocket and SSE handlers:
r := router.NewRouter()
// Add your async handlers...
r.WebSocket("/ws/chat", chatHandler)
r.SSE("/sse/notifications", notificationHandler)
// Enable AsyncAPI documentation
r.EnableAsyncAPI()Visit these URLs to access your AsyncAPI documentation:
/asyncapi/docs- AsyncAPI documentation hub/asyncapi- Raw AsyncAPI JSON specification
Automatic Schema Generation
Steel generates AsyncAPI schemas directly from your Go types used in WebSocket and SSE handlers:
WebSocket Message Types
type ChatMessage struct {
UserID int `json:"user_id" description:"ID of the user sending the message"`
Message string `json:"message" description:"Chat message content"`
Room string `json:"room" description:"Chat room identifier"`
Timestamp time.Time `json:"timestamp" description:"Message timestamp"`
Type string `json:"type" description:"Message type (text, image, file)"`
}
type ChatResponse struct {
MessageID string `json:"message_id" description:"Unique message identifier"`
UserID int `json:"user_id" description:"User who sent the message"`
Username string `json:"username" description:"Display name of the user"`
Message string `json:"message" description:"Chat message content"`
Room string `json:"room" description:"Chat room identifier"`
Timestamp time.Time `json:"timestamp" description:"Message timestamp"`
Edited bool `json:"edited" description:"Whether the message was edited"`
}
r.WebSocket("/ws/chat/:room", func(conn *router.WSConnection, message ChatMessage) (*ChatResponse, error) {
// Handler implementation...
return &ChatResponse{...}, nil
}, router.WithAsyncSummary("Real-time Chat"),
router.WithAsyncDescription("WebSocket endpoint for real-time chat communication"),
router.WithAsyncTags("chat", "messaging"))SSE Event Types
type NotificationEvent struct {
ID string `json:"id" description:"Unique notification ID"`
Type string `json:"type" description:"Notification type"`
Title string `json:"title" description:"Notification title"`
Message string `json:"message" description:"Notification message"`
Data map[string]interface{} `json:"data,omitempty" description:"Additional notification data"`
Timestamp time.Time `json:"timestamp" description:"Notification timestamp"`
Priority string `json:"priority" description:"Notification priority (low, normal, high)"`
}
r.SSE("/sse/notifications/:user_id", func(conn *router.SSEConnection, params struct {
UserID int `path:"user_id" description:"User ID for notifications"`
}) error {
// Handler implementation...
return nil
}, router.WithAsyncSummary("User Notifications"),
router.WithAsyncDescription("Server-sent events for real-time user notifications"),
router.WithAsyncTags("notifications", "sse"))Generated AsyncAPI Specification
The above handlers generate this AsyncAPI specification:
asyncapi: '2.6.0'
info:
title: Steel API
version: 1.0.0
description: API documentation generated by Steel
servers:
production:
url: ws://localhost:8080
protocol: ws
description: WebSocket server
channels:
/ws/chat/{room}:
description: WebSocket endpoint for real-time chat communication
parameters:
room:
description: Chat room identifier
schema:
type: string
subscribe:
summary: Real-time Chat
description: Subscribe to chat messages
tags:
- name: chat
- name: messaging
message:
contentType: application/json
payload:
$ref: '#/components/schemas/ChatMessage'
publish:
summary: Real-time Chat Response
description: Send chat messages
tags:
- name: chat
- name: messaging
message:
contentType: application/json
payload:
$ref: '#/components/schemas/ChatResponse'
/sse/notifications/{user_id}:
description: Server-sent events for real-time user notifications
parameters:
user_id:
description: User ID for notifications
schema:
type: integer
subscribe:
summary: User Notifications
description: Subscribe to user notifications
tags:
- name: notifications
- name: sse
message:
contentType: text/event-stream
payload:
$ref: '#/components/schemas/SSEMessage'
components:
schemas:
ChatMessage:
type: object
properties:
user_id:
type: integer
description: ID of the user sending the message
message:
type: string
description: Chat message content
room:
type: string
description: Chat room identifier
timestamp:
type: string
format: date-time
description: Message timestamp
type:
type: string
description: Message type (text, image, file)
required:
- user_id
- message
- room
- timestamp
- type
ChatResponse:
type: object
properties:
message_id:
type: string
description: Unique message identifier
user_id:
type: integer
description: User who sent the message
username:
type: string
description: Display name of the user
message:
type: string
description: Chat message content
room:
type: string
description: Chat room identifier
timestamp:
type: string
format: date-time
description: Message timestamp
edited:
type: boolean
description: Whether the message was edited
required:
- message_id
- user_id
- username
- message
- room
- timestamp
- editedAdvanced Features
Complex Message Types
Handle complex nested types and arrays:
type GameState struct {
GameID string `json:"game_id" description:"Unique game identifier"`
Players []Player `json:"players" description:"List of players in the game"`
Board GameBoard `json:"board" description:"Current game board state"`
Turn string `json:"turn" description:"Current player's turn"`
Status GameStatus `json:"status" description:"Game status"`
Metadata GameMeta `json:"metadata" description:"Game metadata"`
}
type Player struct {
ID string `json:"id" description:"Player ID"`
Name string `json:"name" description:"Player display name"`
Color string `json:"color" description:"Player color"`
Score int `json:"score" description:"Player score"`
Online bool `json:"online" description:"Whether player is online"`
LastSeen time.Time `json:"last_seen" description:"Last activity timestamp"`
}
type GameBoard struct {
Width int `json:"width" description:"Board width"`
Height int `json:"height" description:"Board height"`
Cells [][]Cell `json:"cells" description:"2D array of board cells"`
}
type Cell struct {
X int `json:"x" description:"Cell X coordinate"`
Y int `json:"y" description:"Cell Y coordinate"`
Type string `json:"type" description:"Cell type"`
Owner string `json:"owner,omitempty" description:"Cell owner (player ID)"`
Value interface{} `json:"value,omitempty" description:"Cell value"`
}
r.WebSocket("/ws/game/:game_id", func(conn *router.WSConnection, action GameAction) (*GameState, error) {
// Complex game logic...
return updateGameState(action)
}, router.WithAsyncSummary("Game State Management"),
router.WithAsyncDescription("Real-time multiplayer game state synchronization"))Message Validation
Document validation constraints in your AsyncAPI spec:
type UserProfileUpdate struct {
UserID int `json:"user_id" description:"User identifier" min:"1"`
DisplayName string `json:"display_name" description:"User display name" min:"2" max:"50"`
Email string `json:"email" description:"User email address" format:"email"`
Avatar string `json:"avatar,omitempty" description:"Avatar URL" format:"uri"`
Bio string `json:"bio,omitempty" description:"User biography" max:"500"`
Age int `json:"age,omitempty" description:"User age" min:"13" max:"120"`
Location string `json:"location,omitempty" description:"User location" max:"100"`
Website string `json:"website,omitempty" description:"Personal website" format:"uri"`
}Event Types and Patterns
Document different event patterns:
// Broadcast event
type SystemBroadcast struct {
Type string `json:"type" description:"Broadcast type"`
Message string `json:"message" description:"Broadcast message"`
Timestamp time.Time `json:"timestamp" description:"Broadcast timestamp"`
Data map[string]interface{} `json:"data,omitempty" description:"Additional data"`
Priority string `json:"priority" description:"Message priority (info, warning, error)"`
}
// Request-response pattern
type DataRequest struct {
RequestID string `json:"request_id" description:"Unique request identifier"`
Query string `json:"query" description:"Data query string"`
Filters map[string]interface{} `json:"filters,omitempty" description:"Query filters"`
Limit int `json:"limit,omitempty" description:"Result limit" max:"1000"`
}
type DataResponse struct {
RequestID string `json:"request_id" description:"Original request identifier"`
Data []interface{} `json:"data" description:"Query results"`
Total int `json:"total" description:"Total available results"`
HasMore bool `json:"has_more" description:"Whether more results are available"`
Timestamp time.Time `json:"timestamp" description:"Response timestamp"`
}
// Error event
type ErrorEvent struct {
Code string `json:"code" description:"Error code"`
Message string `json:"message" description:"Error message"`
Details interface{} `json:"details,omitempty" description:"Error details"`
Timestamp time.Time `json:"timestamp" description:"Error timestamp"`
RequestID string `json:"request_id,omitempty" description:"Related request ID"`
}Handler Documentation
Use handler options to enrich your AsyncAPI documentation:
r.WebSocket("/ws/trading/:symbol", tradingHandler,
router.WithAsyncSummary("Real-time Trading"),
router.WithAsyncDescription(`
WebSocket endpoint for real-time trading operations.
**Features:**
- Live price updates
- Order book streaming
- Trade execution
- Portfolio updates
**Rate Limits:**
- 100 messages per second per connection
- Maximum 10 subscriptions per connection
**Authentication:**
- Requires valid API key in query parameter
- JWT token for user identification
`),
router.WithAsyncTags("trading", "finance", "real-time"))
r.SSE("/sse/market-data", marketDataHandler,
router.WithAsyncSummary("Market Data Stream"),
router.WithAsyncDescription(`
Server-sent events for real-time market data.
**Data Types:**
- Price updates every 100ms
- Volume updates every second
- News events as they occur
- Market status changes
**Subscription Management:**
- Use query parameters to filter symbols
- Automatic reconnection on connection loss
- Heartbeat every 30 seconds
`),
router.WithAsyncTags("market-data", "finance", "streaming"))Security Documentation
Document security requirements in your AsyncAPI spec:
// Authentication handler wrapper
func requireAuth(handler interface{}) interface{} {
// Implementation that validates tokens
return handler
}
r.WebSocket("/ws/secure/admin", requireAuth(adminHandler),
router.WithAsyncSummary("Admin WebSocket"),
router.WithAsyncDescription(`
Secure WebSocket endpoint for administrative operations.
**Authentication Required:**
- Bearer token in Authorization header OR
- API key in 'token' query parameter
**Permissions:**
- Admin role required
- Scope: 'admin:read' and 'admin:write'
**Rate Limits:**
- 50 messages per minute
- Burst limit: 10 messages per second
`),
router.WithAsyncTags("admin", "secure"))Production Considerations
Performance Documentation
Document performance characteristics:
r.WebSocket("/ws/high-frequency/:channel", hfHandler,
router.WithAsyncSummary("High-Frequency Data"),
router.WithAsyncDescription(`
High-frequency WebSocket for real-time data streaming.
**Performance Characteristics:**
- Message rate: up to 10,000 messages/second
- Latency: < 1ms average
- Throughput: 100MB/s per connection
- Memory usage: ~50MB per 1000 active connections
**Optimization Notes:**
- Uses binary message format for efficiency
- Automatic message batching for high volume
- Connection pooling recommended for clients
- Keep-alive: 30 seconds
`),
router.WithAsyncTags("high-frequency", "performance"))Error Handling Documentation
Document error scenarios and recovery:
r.SSE("/sse/critical-alerts", alertHandler,
router.WithAsyncSummary("Critical System Alerts"),
router.WithAsyncDescription(`
Server-sent events for critical system alerts.
**Error Handling:**
- Connection timeout: 5 minutes
- Automatic retry with exponential backoff
- Maximum retries: 5 attempts
- Fallback to HTTP polling if SSE fails
**Message Delivery:**
- Guaranteed delivery for critical alerts
- Message deduplication based on alert ID
- Replay capability for missed messages
- Persistence: 24 hours
**Client Recommendations:**
- Implement exponential backoff: 1s, 2s, 4s, 8s, 16s
- Check Last-Event-ID header for reconnection
- Monitor connection health with heartbeat events
- Implement circuit breaker pattern
`),
router.WithAsyncTags("alerts", "critical", "reliability"))Comparison with OpenAPI
| Feature | OpenAPI (REST) | AsyncAPI (WebSocket/SSE) |
|---|---|---|
| Protocol | HTTP | WebSocket, SSE, MQTT, etc. |
| Communication | Request-Response | Pub/Sub, Streaming |
| Message Format | JSON, XML, etc. | JSON, Binary, Text |
| Operations | GET, POST, PUT, DELETE | Subscribe, Publish |
| Security | Bearer tokens, API keys | Same + Connection-level auth |
| Documentation | Swagger UI, ReDoc | AsyncAPI Studio, Playground |
Best Practices
Documentation Tip: Use comprehensive descriptions and examples in your AsyncAPI documentation to help developers understand message flows and integration patterns.
1. Consistent Message Formats
// Use consistent wrapper formats
type WebSocketMessage[T any] struct {
Type string `json:"type" description:"Message type identifier"`
Timestamp time.Time `json:"timestamp" description:"Message timestamp"`
Data T `json:"data" description:"Message payload"`
ID string `json:"id,omitempty" description:"Message ID for tracking"`
}
type SSEEventData[T any] struct {
EventType string `json:"event_type" description:"Event type"`
Timestamp time.Time `json:"timestamp" description:"Event timestamp"`
Payload T `json:"payload" description:"Event payload"`
Source string `json:"source" description:"Event source"`
}2. Version Your Async APIs
r.WebSocket("/ws/v1/chat", chatHandlerV1,
router.WithAsyncSummary("Chat API v1"),
router.WithAsyncTags("v1", "chat"))
r.WebSocket("/ws/v2/chat", chatHandlerV2,
router.WithAsyncSummary("Chat API v2"),
router.WithAsyncDescription("Enhanced chat with reactions and threading"),
router.WithAsyncTags("v2", "chat"))3. Document Message Flows
r.WebSocket("/ws/collaboration", collaborationHandler,
router.WithAsyncSummary("Document Collaboration"),
router.WithAsyncDescription(`
Real-time collaborative document editing.
**Message Flow:**
1. Client sends 'join_document' message
2. Server responds with current document state
3. Client sends edit operations as 'document_edit' messages
4. Server broadcasts transformed operations to other clients
5. Client sends 'leave_document' when done
**Conflict Resolution:**
- Uses Operational Transformation (OT)
- Edit operations include vector clocks
- Server maintains operation history
`),
router.WithAsyncTags("collaboration", "documents"))AsyncAPI integration in Steel provides the same level of automatic documentation for your real-time APIs as OpenAPI does for REST APIs, ensuring your asynchronous endpoints are well-documented and easy to integrate with.