Skip to Content
Steel is in alpha 🎉
OpenAPI IntegrationAsyncAPI Integration

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 - edited

Advanced 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

FeatureOpenAPI (REST)AsyncAPI (WebSocket/SSE)
ProtocolHTTPWebSocket, SSE, MQTT, etc.
CommunicationRequest-ResponsePub/Sub, Streaming
Message FormatJSON, XML, etc.JSON, Binary, Text
OperationsGET, POST, PUT, DELETESubscribe, Publish
SecurityBearer tokens, API keysSame + Connection-level auth
DocumentationSwagger UI, ReDocAsyncAPI 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.

Last updated on