Usage Examples
This guide provides comprehensive, real-world examples demonstrating how to use the Optave SDK in various scenarios and frameworks. Each example includes complete, production-ready code with error handling, best practices, and explanations.
Basic Client Setup (Browser)
Complete example for browser applications using the token provider pattern:
import OptaveJavaScriptSDK from '@optave/client-sdk';
// Initialize SDK
const client = new OptaveJavaScriptSDK({
websocketUrl: 'wss://ws-{{tenant}}.oco.optave.{{tld}}',
authTransport: 'subprotocol',
// Token provider - fetches tokens from your secure backend
tokenProvider: async () => {
// Replace '/api/optave-token' with your actual backend endpoint
const response = await fetch('/api/optave-token', {
method: 'POST',
credentials: 'include', // Include session cookies
headers: {
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`Failed to fetch token: ${response.status}`);
}
const { token } = await response.json();
return token;
},
// Optional: Configure timeouts
connectionTimeoutMs: 30000,
requestTimeoutMs: 45000
});
// Set up event listeners
client.on('open', () => {
console.log('✓ Connected to Optave');
});
client.on('close', (event) => {
console.log('✗ Disconnected:', event.code);
});
client.on('error', (error) => {
console.error('SDK Error:', error.message);
});
// Connect
try {
await client.openConnection();
console.log('Ready to send messages');
} catch (error) {
console.error('Connection failed:', error);
}
Basic Client Setup (Server)
Complete example for Node.js server applications using client credentials:
import OptaveJavaScriptSDK from '@optave/client-sdk/server';
import dotenv from 'dotenv';
dotenv.config();
// Initialize SDK
const client = new OptaveJavaScriptSDK({
websocketUrl: process.env.OPTAVE_WEBSOCKET_URL,
authenticationUrl: process.env.OPTAVE_AUTHENTICATION_URL,
clientId: process.env.OPTAVE_CLIENT_ID,
clientSecret: process.env.OPTAVE_CLIENT_SECRET
});
// Set up event listeners
client.on('open', () => {
console.log('✓ Server connected to Optave');
});
client.on('error', (error) => {
console.error('SDK Error:', error);
});
// Authenticate and connect
try {
const token = await client.authenticate();
await client.openConnection(token);
console.log('Ready to process requests');
} catch (error) {
console.error('Failed to initialize:', error);
process.exit(1);
}
React Integration
Complete React integration with hooks and state management:
// hooks/useOptaveClient.js
import { useState, useEffect, useCallback, useRef } from 'react';
import OptaveJavaScriptSDK from '@optave/client-sdk';
export function useOptaveClient() {
const [isConnected, setIsConnected] = useState(false);
const [isConnecting, setIsConnecting] = useState(false);
const [error, setError] = useState(null);
const clientRef = useRef(null);
// Initialize client on mount
useEffect(() => {
const client = new OptaveJavaScriptSDK({
websocketUrl: import.meta.env.VITE_OPTAVE_WEBSOCKET_URL,
authTransport: 'subprotocol',
tokenProvider: async () => {
// Replace '/api/optave-token' with your actual backend endpoint
const response = await fetch('/api/optave-token', {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('Token fetch failed');
}
const { token } = await response.json();
return token;
}
});
// Event handlers
client.on('open', () => {
setIsConnected(true);
setIsConnecting(false);
setError(null);
});
client.on('close', () => {
setIsConnected(false);
});
client.on('error', (err) => {
setError(err);
setIsConnecting(false);
});
clientRef.current = client;
// Connect
setIsConnecting(true);
client.openConnection().catch((err) => {
setError(err);
setIsConnecting(false);
});
// Cleanup on unmount
return () => {
if (clientRef.current) {
clientRef.current.cleanup();
}
};
}, []);
// Send message helper
const sendMessage = useCallback(async (action, params) => {
if (!clientRef.current || !isConnected) {
throw new Error('Client not connected');
}
try {
const response = await clientRef.current[action](params);
return response;
} catch (error) {
console.error(`${action} failed:`, error);
throw error;
}
}, [isConnected]);
return {
client: clientRef.current,
isConnected,
isConnecting,
error,
sendMessage
};
}
// components/ChatInterface.jsx
import React, { useState } from 'react';
import { useOptaveClient } from '../hooks/useOptaveClient';
function ChatInterface() {
const { isConnected, isConnecting, error, sendMessage } = useOptaveClient();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [loading, setLoading] = useState(false);
const handleSend = async () => {
if (!input.trim() || !isConnected) return;
const userMessage = input;
setInput('');
setMessages(prev => [...prev, { role: 'user', content: userMessage }]);
try {
setLoading(true);
const response = await sendMessage('interaction', {
request: {
scope: {
conversations: [{
messages: [
...messages,
{ content: userMessage, participantId: 'user-1', timestamp: new Date().toISOString() }
]
}]
}
}
});
setMessages(prev => [
...prev,
{ role: 'assistant', content: response.data.message }
]);
} catch (err) {
console.error('Failed to send message:', err);
setMessages(prev => [
...prev,
{ role: 'error', content: 'Failed to send message. Please try again.' }
]);
} finally {
setLoading(false);
}
};
if (isConnecting) {
return <div>Connecting to Optave...</div>;
}
if (error) {
return <div>Error: {error.message}</div>;
}
return (
<div className="chat-interface">
<div className="status">
{isConnected ? '✓ Connected' : '✗ Disconnected'}
</div>
<div className="messages">
{messages.map((msg, index) => (
<div key={index} className={`message ${msg.role}`}>
{msg.content}
</div>
))}
</div>
<div className="input-area">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSend()}
disabled={!isConnected || loading}
placeholder="Type a message..."
/>
<button onClick={handleSend} disabled={!isConnected || loading}>
{loading ? 'Sending...' : 'Send'}
</button>
</div>
</div>
);
}
export default ChatInterface;
Salesforce Lightning Web Component Integration
Complete Salesforce LWC integration with the Optave SDK using Static Resources:
note
Upload the Optave SDK UMD bundle (browser.umd.js) as a Static Resource named OptaveSDK in Salesforce.
// optaveChatComponent.js - Lightning Web Component
import { LightningElement, track, api } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import OPTAVE_SDK from '@salesforce/resourceUrl/OptaveSDK'; // Static resource
import getOptaveToken from '@salesforce/apex/OptaveController.getToken';
export default class OptaveChatComponent extends LightningElement {
@track connectionStatus = 'Disconnected';
@track messages = [];
@api recordId; // Current record context
sdk;
async connectedCallback() {
try {
// Load Optave SDK from static resource
await loadScript(this, OPTAVE_SDK);
// Initialize SDK
await this.initializeOptaveSDK();
} catch (error) {
console.error('Failed to load Optave SDK:', error);
this.connectionStatus = 'Failed to load';
}
}
async initializeOptaveSDK() {
// Access SDK from global scope - Lightning Locker uses globalThis
const OptaveSDK = globalThis.OptaveJavaScriptSDK || window.OptaveJavaScriptSDK;
if (!OptaveSDK) {
throw new Error('Optave SDK not found in global scope');
}
// Initialize with LWC-specific configuration
this.sdk = new OptaveSDK({
websocketUrl: 'wss://ws-{{tenant}}.oco.optave.{{tld}}',
authTransport: 'subprotocol',
tokenProvider: async () => {
// Call Apex method via imperative call
const result = await getOptaveToken();
return result;
},
strictValidation: false
});
// Set up event handlers
this.setupEventHandlers();
// Connect to Optave WebSocket
await this.sdk.openConnection();
console.log('✅ LWC: Optave SDK initialized');
}
setupEventHandlers() {
this.sdk.on('open', () => {
this.connectionStatus = 'Connected';
console.log('✅ LWC: WebSocket connected');
});
this.sdk.on('message', (payload) => {
this.handleMessage(JSON.parse(payload));
});
this.sdk.on('error', (error) => {
console.error('❌ LWC: SDK error:', error);
this.connectionStatus = `Error: ${error.message}`;
});
this.sdk.on('close', () => {
this.connectionStatus = 'Disconnected';
console.log('🔌 LWC: Connection closed');
});
}
handleMessage(message) {
// Update reactive properties
this.messages = [
...this.messages,
{
id: message.headers?.correlationId || Date.now(),
content: message.payload?.content || 'Message received',
timestamp: new Date().toLocaleTimeString(),
type: 'ai'
}
];
}
handleSendMessage(event) {
const messageText = event.target.value;
if (this.sdk && messageText) {
this.sdk.interaction({
request: {
connections: {
threadId: `lwc-${this.recordId}-${Date.now()}`
},
scope: {
conversations: [
{
conversationId: 'conv_' + Date.now(),
participants: [
{
participantId: 'salesforce_user_' + Date.now(),
role: 'user',
displayName: 'Salesforce User'
}
],
messages: [
{
participantId: 'salesforce_user_' + Date.now(),
content: messageText,
timestamp: new Date().toISOString()
}
],
metadata: {}
}
]
}
}
});
// Clear input
event.target.value = '';
}
}
disconnectedCallback() {
// Cleanup on component destroy
if (this.sdk) {
this.sdk.closeConnection();
}
}
get isConnected() {
return this.connectionStatus === 'Connected';
}
}
Common Patterns
Check Connection Before Sending
if (!client.wss || client.wss.readyState !== 1) {
throw new Error('Not connected to WebSocket');
}
const response = await client.interaction(params);
Retry on Error
async function sendWithRetry(action, params, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await client[action](params);
} catch (error) {
if (attempt === maxRetries) throw error;
const delay = Math.pow(2, attempt) * 1000;
console.log(`Retry ${attempt} after ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
const response = await sendWithRetry('interaction', params);