WebSocket Client
The WebSocket client provides real-time communication with the Heimdall API. It's designed for client-side use only.
createWebSocket
Create a WebSocket connection with auto-reconnect support.
import { createWebSocket } from "@/lib/api";
const ws = createWebSocket("/v1/ws/user", {
accessToken: session.accessToken,
});
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
endpoint | string | Yes | WebSocket endpoint (e.g., /v1/ws/user) |
config | WebSocketConfig | No | Configuration options |
Config Options
interface WebSocketConfig {
accessToken?: string;
autoReconnect?: boolean;
reconnectDelay?: number;
maxReconnectAttempts?: number;
}
| Option | Type | Default | Description |
|---|---|---|---|
accessToken | string | - | OAuth access token for auth |
autoReconnect | boolean | true | Auto-reconnect on disconnect |
reconnectDelay | number | 3000 | Delay between reconnects (ms) |
maxReconnectAttempts | number | 5 | Max reconnection attempts |
Returns
interface WebSocketClient {
socket: WebSocket;
send: (data: unknown) => void;
close: () => void;
onMessage: (handler: (data: unknown) => void) => void;
onError: (handler: (error: Event) => void) => void;
onClose: (handler: (event: CloseEvent) => void) => void;
}
Example
const ws = createWebSocket("/v1/ws/user", {
accessToken: session.accessToken,
autoReconnect: true,
reconnectDelay: 5000,
maxReconnectAttempts: 3,
});
ws.onMessage((data) => {
console.log("Received:", data);
});
ws.onError((error) => {
console.error("WebSocket error:", error);
});
ws.onClose((event) => {
console.log("Connection closed:", event.code);
});
// Send a message
ws.send({ type: "subscribe", channel: "updates" });
// Close when done
ws.close();
useWebSocket Hook
React hook for WebSocket connections with state management.
import { useWebSocket } from "@/lib/api";
function LiveUpdates() {
const { isConnected, lastMessage, send, connect, disconnect } = useWebSocket(
"/v1/ws/updates",
{ accessToken: session.accessToken }
);
return (
<div>
<p>Status: {isConnected ? "Connected" : "Disconnected"}</p>
<button onClick={() => send({ type: "ping" })}>Ping</button>
</div>
);
}
Options
interface UseWebSocketOptions extends WebSocketConfig {
immediate?: boolean; // Connect immediately (default: true)
}
Returns
interface UseWebSocketReturn {
isConnected: boolean;
lastMessage: unknown;
send: (data: unknown) => void;
connect: () => void;
disconnect: () => void;
}
Example with Message Handling
function NotificationListener() {
const { isConnected, lastMessage } = useWebSocket("/v1/ws/notifications", {
accessToken: session.accessToken,
});
useEffect(() => {
if (lastMessage) {
const msg = lastMessage as { type: string; payload: unknown };
switch (msg.type) {
case "notification":
showNotification(msg.payload);
break;
case "update":
refreshData();
break;
}
}
}, [lastMessage]);
return <ConnectionStatus connected={isConnected} />;
}
Server Message Types
The library includes TypeScript types for all server-sent WebSocket messages.
User Status Messages
Sent when user account status changes:
import type { UserStatusMessage } from "@/lib/api";
// Account deleted
interface AccountDeletedMessage {
type: "accountDeleted";
userId: string;
reason?: string;
timestamp: string;
}
// Account banned
interface AccountBannedMessage {
type: "accountBanned";
userId: string;
reason?: string;
expiresAt?: string;
isPermanent: boolean;
timestamp: string;
}
// Session revoked
interface SessionRevokedMessage {
type: "sessionRevoked";
userId: string;
sessionId?: string;
reason?: string;
timestamp: string;
}
// Force logout
interface ForceLogoutMessage {
type: "forceLogout";
userId: string;
reason?: string;
timestamp: string;
}
Permission Messages
Sent when user permissions change:
import type { PermissionMessage } from "@/lib/api";
// Roles updated
interface RolesUpdatedMessage {
type: "rolesUpdated";
userId: string;
roles: string[];
timestamp: string;
}
// Permissions updated
interface PermissionsUpdatedMessage {
type: "permissionsUpdated";
userId: string;
permissions: string[];
timestamp: string;
}
// Role permissions changed (system-wide)
interface RolePermissionsChangedMessage {
type: "rolePermissionsChanged";
roleId: string;
roleName: string;
timestamp: string;
}
Account Link Messages
Sent when account linking events occur:
import type { AccountLinkMessage } from "@/lib/api";
// Email link verified
interface EmailLinkVerifiedMessage {
type: "emailLinkVerified";
userId: string;
email: string;
platformAccountId: string;
timestamp: string;
}
// Email change verified
interface EmailChangeVerifiedMessage {
type: "emailChangeVerified";
userId: string;
oldEmail: string;
newEmail: string;
timestamp: string;
}
Handling Server Messages
import type { ServerMessage } from "@/lib/api";
ws.onMessage((data: unknown) => {
const message = data as ServerMessage;
switch (message.type) {
case "accountBanned":
handleBan(message);
break;
case "forceLogout":
signOut();
break;
case "rolesUpdated":
refreshPermissions();
break;
case "emailLinkVerified":
refreshAccounts();
break;
}
});
wsConfig
Configuration object for the WebSocket client.
import { wsConfig } from "@/lib/api";
console.log(wsConfig.wsUrl); // "ws://localhost:3000"
Best Practices
Cleanup on Unmount
Always close WebSocket connections when components unmount:
useEffect(() => {
const ws = createWebSocket("/v1/ws/updates", { accessToken });
ws.onMessage(handleMessage);
return () => {
ws.close(); // Important: cleanup
};
}, [accessToken]);
The useWebSocket hook handles this automatically.
Reconnection Handling
The client auto-reconnects by default. Customize behavior:
const ws = createWebSocket("/v1/ws/user", {
accessToken,
autoReconnect: true,
reconnectDelay: 5000, // Wait 5s between attempts
maxReconnectAttempts: 10, // Give up after 10 attempts
});
Connection State UI
function ConnectionIndicator() {
const { isConnected } = useWebSocket("/v1/ws/status", { accessToken });
return (
<div className={isConnected ? "text-green-500" : "text-red-500"}>
{isConnected ? "Connected" : "Reconnecting..."}
</div>
);
}