13 KiB
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
- Overview
- Prerequisites
- Installation
- Quick Start
- Basic Examples
- Advanced Usage
- 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:
- NATS Server running (or accessible)
- HTTP File Server (optional, for large payloads > 1MB)
- 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
- Copy
src/nats_bridge.pyto your device - Install dependencies:
For Python (desktop):
pip install nats-py
For Micropython:
urequestsfor HTTP requestsbase64for base64 encoding (built-in)jsonfor 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, fileserverDownloadHandler)
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
- Explore the test directory for more examples
- Check the documentation for advanced configuration options
- 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