Files
NATSBridge/examples/tutorial.md
2026-02-24 20:09:10 +07:00

13 KiB

NATSBridge Tutorial

A step-by-step guide to get started with NATSBridge - a high-performance, bi-directional data bridge for Julia, JavaScript, and Python/Micropython.

Table of Contents

  1. Overview
  2. Prerequisites
  3. Installation
  4. Quick Start
  5. Basic Examples
  6. Advanced Usage
  7. Cross-Platform Communication

Overview

NATSBridge enables seamless communication between Julia, JavaScript, and Python/Micropython applications through NATS, with automatic transport selection based on payload size:

  • Direct Transport: Payloads < 1MB are sent directly via NATS (Base64 encoded)
  • Link Transport: Payloads >= 1MB are uploaded to an HTTP file server and referenced via URL

Supported Payload Types

Type Description
text Plain text strings
dictionary JSON-serializable dictionaries
table Tabular data (Arrow IPC format)
image Image data (PNG, JPG bytes)
audio Audio data (WAV, MP3 bytes)
video Video data (MP4, AVI bytes)
binary Generic binary data

Prerequisites

Before you begin, ensure you have:

  1. NATS Server running (or accessible)
  2. HTTP File Server (optional, for large payloads > 1MB)
  3. One of the supported platforms: Julia, JavaScript (Node.js), or Python/Micropython

Installation

Julia

using Pkg
Pkg.add("NATS")
Pkg.add("Arrow")
Pkg.add("JSON3")
Pkg.add("HTTP")
Pkg.add("UUIDs")
Pkg.add("Dates")

JavaScript

npm install nats.js apache-arrow uuid base64-url

Python/Micropython

  1. Copy src/nats_bridge.py to your device
  2. Install dependencies:

For Python (desktop):

pip install nats-py

For Micropython:

  • urequests for HTTP requests
  • base64 for base64 encoding (built-in)
  • json for JSON handling (built-in)

Quick Start

Step 1: Start NATS Server

docker run -p 4222:4222 nats:latest

Step 2: Start HTTP File Server (Optional)

# Create a directory for file uploads
mkdir -p /tmp/fileserver

# Use Python's built-in server
python3 -m http.server 8080 --directory /tmp/fileserver

Step 3: Send Your First Message

Python/Micropython

from nats_bridge import smartsend

# Send a text message (is_publish=True by default)
data = [("message", "Hello World", "text")]
env, env_json_str = smartsend("/chat/room1", data, nats_url="nats://localhost:4222")
print("Message sent!")

# Or use is_publish=False to get envelope and JSON without publishing
env, env_json_str = smartsend("/chat/room1", data, nats_url="nats://localhost:4222", is_publish=False)
# env: MessageEnvelope object
# env_json_str: JSON string for publishing to NATS

JavaScript

const { smartsend } = require('./src/NATSBridge');

// Send a text message (isPublish=true by default)
await smartsend("/chat/room1", [
    { dataname: "message", data: "Hello World", type: "text" }
], { natsUrl: "nats://localhost:4222" });

console.log("Message sent!");

// Or use isPublish=false to get envelope and JSON without publishing
const { env, env_json_str } = await smartsend("/chat/room1", [
    { dataname: "message", data: "Hello World", type: "text" }
], { natsUrl: "nats://localhost:4222", isPublish: false });
// env: MessageEnvelope object
// env_json_str: JSON string for publishing to NATS

Julia

using NATSBridge

# Send a text message
data = [("message", "Hello World", "text")]
env, env_json_str = smartsend("/chat/room1", data, nats_url="nats://localhost:4222")
# env: msgEnvelope_v1 object with all metadata and payloads
# env_json_str: JSON string representation of the envelope for publishing
println("Message sent!")

Step 4: Receive Messages

Python/Micropython

from nats_bridge import smartreceive

# Receive and process message
env = smartreceive(msg)
for dataname, data, type in env["payloads"]:
    print(f"Received {dataname}: {data}")

JavaScript

const { smartreceive } = require('./src/NATSBridge');

// Receive and process message
const env = await smartreceive(msg);
for (const payload of env.payloads) {
    console.log(`Received ${payload.dataname}: ${payload.data}`);
}

Julia

using NATSBridge

# Receive and process message
env = smartreceive(msg; fileserver_download_handler=_fetch_with_backoff)
for (dataname, data, type) in env["payloads"]
    println("Received $dataname: $data")
end

Basic Examples

Example 1: Sending a Dictionary

Python/Micropython

from nats_bridge import smartsend

# Create configuration dictionary
config = {
    "wifi_ssid": "MyNetwork",
    "wifi_password": "password123",
    "update_interval": 60
}

# Send as dictionary type
data = [("config", config, "dictionary")]
env, env_json_str = smartsend("/device/config", data, nats_url="nats://localhost:4222")

JavaScript

const { smartsend } = require('./src/NATSBridge');

const config = {
    wifi_ssid: "MyNetwork",
    wifi_password: "password123",
    update_interval: 60
};

const { env, env_json_str } = await smartsend("/device/config", [
    { dataname: "config", data: config, type: "dictionary" }
]);

Julia

using NATSBridge

config = Dict(
    "wifi_ssid" => "MyNetwork",
    "wifi_password" => "password123",
    "update_interval" => 60
)

data = [("config", config, "dictionary")]
env, env_json_str = smartsend("/device/config", data)

Example 2: Sending Binary Data (Image)

Python/Micropython

from nats_bridge import smartsend

# Read image file
with open("image.png", "rb") as f:
    image_data = f.read()

# Send as binary type
data = [("user_image", image_data, "binary")]
env, env_json_str = smartsend("/chat/image", data, nats_url="nats://localhost:4222")

JavaScript

const { smartsend } = require('./src/NATSBridge');

// Read image file (Node.js)
const fs = require('fs');
const image_data = fs.readFileSync('image.png');

const { env, env_json_str } = await smartsend("/chat/image", [
    { dataname: "user_image", data: image_data, type: "binary" }
]);

Julia

using NATSBridge

# Read image file
image_data = read("image.png")

data = [("user_image", image_data, "binary")]
env, env_json_str = smartsend("/chat/image", data)

Example 3: Request-Response Pattern

Python/Micropython (Requester)

from nats_bridge import smartsend

# Send command with reply-to
data = [("command", {"action": "read_sensor"}, "dictionary")]
env, env_json_str = smartsend(
    "/device/command",
    data,
    nats_url="nats://localhost:4222",
    reply_to="/device/response",
    reply_to_msg_id="cmd-001"
)
# env: msgEnvelope_v1 object
# env_json_str: JSON string for publishing to NATS

JavaScript (Responder)

const { smartreceive, smartsend } = require('./src/NATSBridge');

// Subscribe to command topic
const sub = nc.subscribe("/device/command");

for await (const msg of sub) {
    const env = await smartreceive(msg);
    
    // Process command
    for (const payload of env.payloads) {
        if (payload.dataname === "command") {
            const command = payload.data;
            
            if (command.action === "read_sensor") {
                // Read sensor and send response
                const response = {
                    sensor_id: "sensor-001",
                    value: 42.5,
                    timestamp: new Date().toISOString()
                };
                
                await smartsend("/device/response", [
                    { dataname: "sensor_data", data: response, type: "dictionary" }
                ], {
                    reply_to: env.replyTo,
                    reply_to_msg_id: env.msgId
                });
            }
        }
    }
}

Advanced Usage

Example 4: Large Payloads (File Server)

For payloads larger than 1MB, NATSBridge automatically uses the file server:

Python/Micropython

from nats_bridge import smartsend
import os

# Create large data (> 1MB)
large_data = os.urandom(2_000_000)  # 2MB of random data

# Send with file server URL
env, env_json_str = smartsend(
    "/data/large",
    [("large_file", large_data, "binary")],
    nats_url="nats://localhost:4222",
    fileserver_url="http://localhost:8080",
    size_threshold=1_000_000
)

# The envelope will contain the download URL
print(f"File uploaded to: {env.payloads[0].data}")

JavaScript

const { smartsend } = require('./src/NATSBridge');

// Create large data (> 1MB)
const largeData = new ArrayBuffer(2_000_000);
const view = new Uint8Array(largeData);
view.fill(42);  // Fill with some data

const { env, env_json_str } = await smartsend("/data/large", [
    { dataname: "large_file", data: largeData, type: "binary" }
], {
    fileserverUrl: "http://localhost:8080",
    sizeThreshold: 1_000_000
});

Julia

using NATSBridge

# Create large data (> 1MB)
large_data = rand(UInt8, 2_000_000)

env, env_json_str = smartsend(
    "/data/large",
    [("large_file", large_data, "binary")],
    fileserver_url="http://localhost:8080"
)

# The envelope will contain the download URL
println("File uploaded to: $(env.payloads[1].data)")

Example 5: Mixed Content (Chat with Text + Image)

NATSBridge supports sending multiple payloads with different types in a single message:

Python/Micropython

from nats_bridge import smartsend

# Read image file
with open("avatar.png", "rb") as f:
    image_data = f.read()

# Send mixed content
data = [
    ("message_text", "Hello with image!", "text"),
    ("user_avatar", image_data, "image")
]

env, env_json_str = smartsend("/chat/mixed", data, nats_url="nats://localhost:4222")

JavaScript

const { smartsend } = require('./src/NATSBridge');

const fs = require('fs');

const { env, env_json_str } = await smartsend("/chat/mixed", [
    {
        dataname: "message_text",
        data: "Hello with image!",
        type: "text"
    },
    {
        dataname: "user_avatar",
        data: fs.readFileSync("avatar.png"),
        type: "image"
    }
]);

Julia

using NATSBridge

image_data = read("avatar.png")

data = [
    ("message_text", "Hello with image!", "text"),
    ("user_avatar", image_data, "image")
]

env, env_json_str = smartsend("/chat/mixed", data)

Example 6: Table Data (Arrow IPC)

For tabular data, NATSBridge uses Apache Arrow IPC format:

Python/Micropython

from nats_bridge import smartsend
import pandas as pd

# Create DataFrame
df = pd.DataFrame({
    "id": [1, 2, 3],
    "name": ["Alice", "Bob", "Charlie"],
    "score": [95, 88, 92]
})

# Send as table type
data = [("students", df, "table")]
env, env_json_str = smartsend("/data/students", data, nats_url="nats://localhost:4222")

Julia

using NATSBridge
using DataFrames

# Create DataFrame
df = DataFrame(
    id = [1, 2, 3],
    name = ["Alice", "Bob", "Charlie"],
    score = [95, 88, 92]
)

data = [("students", df, "table")]
env, env_json_str = smartsend("/data/students", data)

Cross-Platform Communication

NATSBridge enables seamless communication between different platforms:

Julia ↔ JavaScript

Julia Sender

using NATSBridge

# Send dictionary from Julia to JavaScript
config = Dict("step_size" => 0.01, "iterations" => 1000)
data = [("config", config, "dictionary")]
env, env_json_str = smartsend("/analysis/config", data, nats_url="nats://localhost:4222")

JavaScript Receiver

const { smartreceive } = require('./src/NATSBridge');

// Receive dictionary from Julia
const env = await smartreceive(msg);
for (const payload of env.payloads) {
    if (payload.type === "dictionary") {
        console.log("Received config:", payload.data);
        // payload.data = { step_size: 0.01, iterations: 1000 }
    }
}

JavaScript ↔ Python

JavaScript Sender

const { smartsend } = require('./src/NATSBridge');

const { env, env_json_str } = await smartsend("/data/transfer", [
    { dataname: "message", data: "Hello from JS!", type: "text" }
]);

Python Receiver

from nats_bridge import smartreceive

env = smartreceive(msg)
for dataname, data, type in env["payloads"]:
    if type == "text":
        print(f"Received from JS: {data}")

Python ↔ Julia

Python Sender

from nats_bridge import smartsend

data = [("message", "Hello from Python!", "text")]
env, env_json_str = smartsend("/chat/python", data)

Julia Receiver

using NATSBridge

env = smartreceive(msg, fileserverDownloadHandler)
for (dataname, data, type) in env["payloads"]
    if type == "text"
        println("Received from Python: $data")
    end
end

Next Steps

  1. Explore the test directory for more examples
  2. Check the documentation for advanced configuration options
  3. Join the community to share your use cases

Troubleshooting

Connection Issues

  • Ensure NATS server is running: docker ps | grep nats
  • Check firewall settings
  • Verify NATS URL configuration

File Server Issues

  • Ensure file server is running and accessible
  • Check upload permissions
  • Verify file server URL configuration

Serialization Errors

  • Verify data type matches the specified type
  • Check that binary data is in the correct format (bytes/Vector{UInt8})

License

MIT