# NATS Client Module - Documentation ## Overview The `natsClient.js` module provides a comprehensive NATS WebSocket client with automatic reconnection, request/reply messaging, and msghandler integration. ## Features - **Automatic Connection**: Connects to NATS server on demand - **Auto-Reconnection**: Exponential backoff reconnection on connection loss - **Request/Reply**: NATS request/reply pattern for synchronous communication - **Publish/Subscribe**: NATS pub/sub pattern for broadcast messaging - **Msghandler Integration**: Automatic envelope wrapping/unwrapping for message serialization ## API Reference ### Connection Management #### `getNats(opts)` Establishes connection to NATS server. ```javascript import { getNats } from './natsClient.js'; // Connect to default server await getNats(); // Connect to custom server await getNats({ servers: 'wss://nats.yiem.cc' }); ``` **Parameters:** - `opts.servers` (string): NATS server URL (default: `wss://nats.yiem.cc`) **Returns:** `Promise` - NATS connection object #### `connectNATS(brokerUrl, config)` Connects to NATS broker using the provided URL or config. ```javascript import { connectNATS } from './natsClient.js'; await connectNATS('wss://nats.yiem.cc'); // or await connectNATS(null, { nats_server_info: { url: 'wss://nats.yiem.cc' } }); ``` **Parameters:** - `brokerUrl` (string): NATS broker URL - `config` (object): Configuration object with `nats_server_info.url` #### `closeNats()` Closes the NATS connection and unsubscribes from all topics. ```javascript import { closeNats } from './natsClient.js'; await closeNats(); ``` ### Subscription #### `subscribe(subject, onMessage)` Subscribes to a NATS subject and registers a message handler. ```javascript import { subscribe } from './natsClient.js'; const subscription = await subscribe('sommpanion.backend.dbapi.v1.inbox', (message) => { console.log('Received message:', message); }); // Later, to unsubscribe: await subscription.unsubscribe(); ``` **Parameters:** - `subject` (string): NATS subject to subscribe to - `onMessage` (function): Callback function to handle incoming messages **Returns:** `Promise<{unsubscribe: Function}>` - Subscription object with unsubscribe method ### Publishing #### `publish(subject, payload, opts)` Publishes a message to a NATS subject. ```javascript import { publish } from './natsClient.js'; await publish('sommpanion.backend.dbapi.v1.inbox', { cmd: 'search', data: { keyword: 'chardonnay' } }); ``` **Parameters:** - `subject` (string): NATS subject to publish to - `payload` (object|string): Message payload - `opts` (object): Optional configuration ### Request/Reply #### `request(subject, payload, opts)` Sends a request and waits for a reply using NATS request/reply pattern. ```javascript import { request } from './natsClient.js'; const response = await request( 'sommpanion.backend.dbapi.v1.inbox', { functioncall: 'search_wine_table', args: { searchkeyword: 'chardonnay' } } ); ``` **Parameters:** - `subject` (string): NATS subject to send request to - `payload` (string|object): Request payload - `opts` (object): Optional configuration **Returns:** `Promise` - Response from the server #### `reply(subject, handler, opts)` Sets up a reply handler for request/reply pattern. ```javascript import { reply } from './natsClient.js'; const subscription = await reply('sommpanion.backend.dbapi.v1.inbox', (request, msg) => { return { functioncall: request.functioncall, result: 'success', type: 'text' }; }); // Later, to unsubscribe: await subscription.unsubscribe(); ``` **Parameters:** - `subject` (string): NATS subject to listen on - `handler` (function): Handler function that returns response - `opts` (object): Optional configuration **Returns:** `Promise<{unsubscribe: Function}>` - Subscription object with unsubscribe method ### Msghandler Integration #### `sendMsghandlerRequest(subject, functioncall, args, config)` Sends a request using msghandler envelope format for proper message serialization. ```javascript import { sendMsghandlerRequest } from './natsClient.js'; import msghandlerCSR from './msghandler-csr.js'; const response = await sendMsghandlerRequest( 'sommpanion.backend.dbapi.v1.inbox', 'search_wine_table', { searchkeyword: 'chardonnay', searchcolumn: 'wine_name' }, { nats_server_info: { url: 'wss://nats.yiem.cc' } } ); console.log(response.result); // Array of wine records ``` **Parameters:** - `subject` (string): NATS subject to send request to - `functioncall` (string): Backend function to invoke (e.g., `search_wine_table`) - `args` (object): Function arguments - `config` (object): Configuration object containing `nats_server_info.url` **Returns:** `Promise` - Response with `functioncall`, `result`, and `type` fields ### Configuration #### Connection Status Store ```javascript import { connectionStatus } from './natsClient.js'; connectionStatus.subscribe(status => { console.log('NATS status:', status); }); ``` The `connectionStatus` store emits objects with: - `state`: `"connecting"`, `"connected"`, `"error"`, `"disconnected"` - `message`: Human-readable status message ## End-to-End Examples ### Example 1: Search with Msghandler ```javascript import { sendMsghandlerRequest, connectNATS } from './natsClient.js'; async function searchWines(keyword) { await connectNATS(null, { nats_server_info: { url: 'wss://nats.yiem.cc' } }); const response = await sendMsghandlerRequest( 'sommpanion.backend.dbapi.v1.inbox', 'search_wine_table', { searchkeyword: keyword, searchcolumn: 'seo_name' }, { nats_server_info: { url: 'wss://nats.yiem.cc' } } ); if (response && response.result) { console.log('Found wines:', response.result); return response.result; } return []; } ``` ### Example 2: Complete Search Component Integration ```javascript import { sendMsghandlerRequest, getNats } from './natsClient.js'; export async function searchWines(keyword, config) { try { await getNats(); const searchPayload = { searchkeyword: keyword || '*', searchcolumn: 'seo_name' }; const response = await sendMsghandlerRequest( config?.dbservice_subject || 'sommpanion.backend.dbapi.v1.inbox', 'search_wine_table', searchPayload, config ); return response?.result || []; } catch (error) { console.error('Search failed:', error); return []; } } ``` ### Example 3: Basic Publish/Subscribe (No Msghandler) ```javascript import { getNats, subscribe, publish } from './natsClient.js'; async function basicPubSubExample() { // Connect to NATS await getNats(); // Subscribe to a topic const subscription = await subscribe('my.topic', (message) => { console.log('Received:', message); }); // Publish a message await publish('my.topic', { data: 'hello world' }); // Later, unsubscribe await subscription.unsubscribe(); } ``` ### Example 4: Direct Request/Reply (No Msghandler) ```javascript import { getNats, request } from './natsClient.js'; async function directRequestExample() { // Connect to NATS await getNats(); // Send a direct request (no msghandler envelope) const response = await request( 'sommpanion.backend.dbapi.v1.inbox', JSON.stringify({ cmd: 'status', data: {} }) ); console.log('Response:', response); } ``` ## Error Handling ### Connection Errors ```javascript import { connectNATS } from './natsClient.js'; try { await connectNATS('wss://nats.yiem.cc'); } catch (error) { console.error('NATS connection failed:', error); } ``` ### Request/Reply Errors ```javascript import { sendMsghandlerRequest, connectNATS } from './natsClient.js'; try { const response = await sendMsghandlerRequest( 'sommpanion.backend.dbapi.v1.inbox', 'search_wine_table', { searchkeyword: '*', searchcolumn: 'seo_name' }, { nats_server_info: { url: 'wss://nats.yiem.cc' } } ); console.log(response.result); } catch (error) { console.error('Request failed:', error.message); } ``` ## Best Practices 1. **Reuse Connection**: Don't connect/disconnect for each request. Connect once on app startup. 2. **Handle Errors**: Always wrap NATS operations in try-catch blocks. 3. **Use Msghandler**: Always use `sendMsghandlerRequest()` for backend API calls. 4. **Clean Up**: Unsubscribe from topics when components are destroyed. 5. **Configuration**: Read NATS server URL from config for environment-specific configuration. ## Msghandler Message Format ### Request Envelope When using `sendMsghandlerRequest()`, the module automatically: 1. Wraps the payload in msghandler format 2. Uses `dictionary` payload type with `db_request` data name 3. Automatically selects transport based on payload size ### Response Format The response from backend follows this structure: ```json { "correlation_id": "uuid-v4", "msg_id": "uuid-v4", "timestamp": "2026-05-26T...", "send_to": "sommpanion.backend.dbapi.v1.inbox", "msg_purpose": "chat", "sender_name": "agent-backend", "payloads": [ ["db_request", { "functioncall": "search_wine_table", "result": [...], "type": "jsontable" }, "dictionary"] ] } ``` The `sendMsghandlerRequest()` function automatically: - Unwraps the envelope using `smartunpack()` - Extracts the result from `payloads[0][1]` - Returns `{functioncall, result, type}` object ## Troubleshooting ### Connection Issues - Verify NATS server URL in config - Check network connectivity - Ensure NATS server is running ### Request Timeouts - Check backend service is running - Verify subject matches backend listener ### Response Parsing Errors - Ensure backend returns msghandler format - Check payload type matches expected format - Verify `functioncall` and `result` fields exist in response