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

9.7 KiB

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.

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.

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.

import { closeNats } from './natsClient.js';

await closeNats();

Subscription

subscribe(subject, onMessage)

Subscribes to a NATS subject and registers a message handler.

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.

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.

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.

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.

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

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

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

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)

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)

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

import { connectNATS } from './natsClient.js';

try {
  await connectNATS('wss://nats.yiem.cc');
} catch (error) {
  console.error('NATS connection failed:', error);
}

Request/Reply Errors

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:

{
  "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