Files
natsClient_JS/README.md
2026-06-04 13:07:42 +07:00

390 lines
9.7 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 [];
}
}
```
### 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