346 lines
8.8 KiB
Markdown
346 lines
8.8 KiB
Markdown
# 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<nc>` - 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<object>` - 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<object>` - 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 [];
|
|
}
|
|
}
|
|
```
|
|
|
|
## 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
|