WebSockets
ARO provides built-in WebSocket support for real-time bidirectional communication. WebSocket connections are established via HTTP Upgrade on the same port as the HTTP server, making it easy to add live updates to web applications.
Why WebSockets?
Unlike traditional HTTP request-response cycles, WebSocket enables persistent connections where both server and client can push data at any time. This is essential for:
- Real-time updates - Dashboards, notifications, live feeds
- Chat applications - Instant message delivery
- Collaborative features - Multi-user editing
- Event streaming - Logs, sensor data, market feeds
WebSocket Events
WebSocket lifecycle is managed through three event types:
| Event | Handler Name Contains | Triggered When |
|---|---|---|
| Connect | "Connect" | Client completes WebSocket handshake |
| Message | "Message" | Client sends a text message |
| Disconnect | "Disconnect" | Connection closes |
Event Handlers
Handle WebSocket events using the WebSocket Event Handler business activity pattern:
Connection Handler
(Handle WebSocket Connect: WebSocket Event Handler) {
Extract the <connection-id> from the <event: id>.
Log "WebSocket client connected" to the <console>.
Return an <OK: status> for the <connection>.
}
The event object provides:
id- Unique connection identifierpath- WebSocket path (e.g., "/ws")remoteAddress- Client IP address
Message Handler
(Handle WebSocket Message: WebSocket Event Handler) {
Extract the <message> from the <event: message>.
Extract the <connection-id> from the <event: connectionId>.
Log <message> to the <console>.
Return an <OK: status> for the <message>.
}
Disconnection Handler
(Handle WebSocket Disconnect: WebSocket Event Handler) {
Extract the <connection-id> from the <event: connectionId>.
Log "WebSocket client disconnected" to the <console>.
Return an <OK: status> for the <disconnection>.
}
Broadcasting Messages
Send a message to all connected WebSocket clients:
Broadcast the <message> to the <websocket>.
Messages are automatically serialized to JSON if they are objects. This is commonly used when posting new data via HTTP that should be pushed to all connected clients.
Complete Example: Real-Time Chat
Project Structure
WebChat/
├── openapi.yaml # API contract
├── main.aro # Application lifecycle
├── api.aro # HTTP route handlers
├── websocket.aro # WebSocket event handlers
└── templates/
└── index.html # Chat UI
main.aro
(Application-Start: Web Chat) {
Log "Starting Web Chat..." to the <console>.
Start the <http-server> with {}.
Keepalive the <application> for the <events>.
Return an <OK: status> for the <startup>.
}
api.aro
(postMessage: Web Chat API) {
Extract the <body> from the <request: body>.
Extract the <text: message> from the <body>.
Create the <message: Message> with {
message: <text>,
createdAt: <now>
}.
Store the <message> into the <message-repository>.
(* Broadcast to all WebSocket clients *)
Broadcast the <message> to the <websocket>.
Return a <Created: status> with <message>.
}
Client JavaScript
// Connect to WebSocket
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
const ws = new WebSocket(`${protocol}//${location.host}/ws`);
ws.onopen = () => console.log('Connected');
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
displayMessage(message);
};
ws.onclose = () => {
console.log('Disconnected, reconnecting...');
setTimeout(connect, 3000);
};
WebSocket Path
By default, WebSocket connections are accepted on /ws:
const ws = new WebSocket('ws://localhost:8080/ws');
Best Practices
- Use HTTP for commands, WebSocket for updates - Post via HTTP, broadcast via WebSocket
- Handle reconnection on client - Implement automatic reconnect with exponential backoff
- Log connection events - Track connects/disconnects for debugging