Skip to main content

Hello World in TypeScript

The HDDS TypeScript SDK provides native FFI bindings to the hdds-c shared library via koffi. No WebSocket bridge needed -- your application communicates directly over UDP multicast using the RTPS protocol.

Time: ~10 minutes Prerequisites: Node.js >= 18, hdds-c shared library built

Step 1: Build hdds-c

The TypeScript SDK calls the native HDDS library directly. Build it first:

# From the HDDS repository root
git clone https://git.hdds.io/hdds/hdds.git
cd hdds
cargo build --release -p hdds-c

# The library will be at:
# Linux: target/release/libhdds_c.so
# macOS: target/release/libhdds_c.dylib
# Windows: target/release/hdds_c.dll

Step 2: Install the SDK

cd sdk/typescript
npm install

If the native library is not in target/release/ or system paths, set:

export HDDS_LIB_PATH=/path/to/directory/containing/libhdds_c.so

Step 3: Create the Publisher

Create publisher.ts:

import { Participant, QoS, TransportMode } from "@hdds/client";

interface Temperature {
sensor_id: string;
value: number;
timestamp: number;
}

function main(): void {
// Create a participant on the default domain (UDP multicast)
const participant = Participant.create("ts_publisher");

// Reliable QoS with history for late joiners
const qos = QoS.reliable().transientLocal().historyDepth(10);
const writer = participant.createWriter<Temperature>("temperature", qos);

console.log("Publishing temperature readings...");

for (let i = 0; i < 10; i++) {
const temp: Temperature = {
sensor_id: "sensor-001",
value: 22.0 + i * 0.5,
timestamp: Date.now(),
};

writer.writeJson(temp);
console.log(`Published: sensor=${temp.sensor_id}, temp=${temp.value}C`);
}

// Clean up
qos.dispose();
participant.dispose();
}

main();
JSON serialization

writeJson() and takeJson() use JSON encoding, which is not CDR-compatible. This works for TypeScript-to-TypeScript communication. For cross-language interop with C/C++/Python, use write() with CDR2-encoded buffers.

Step 4: Create the Subscriber

Create subscriber.ts:

import { Participant, QoS, WaitSet } from "@hdds/client";

interface Temperature {
sensor_id: string;
value: number;
timestamp: number;
}

function main(): void {
const participant = Participant.create("ts_subscriber");

const qos = QoS.reliable().transientLocal().historyDepth(10);
const reader = participant.createReader<Temperature>("temperature", qos);

// Use a WaitSet to block until data arrives
const waitset = new WaitSet();
waitset.attachReader(reader);

console.log("Waiting for temperature data...");

while (true) {
if (waitset.wait(5.0)) {
const sample = reader.takeJson<Temperature>();
if (sample !== null) {
console.log(
`Received: sensor=${sample.sensor_id}, temp=${sample.value}C`
);
}
} else {
console.log("No data received in 5 seconds, stopping.");
break;
}
}

// Clean up
waitset.dispose();
qos.dispose();
participant.dispose();
}

main();

Step 5: Run

Open two terminals:

# Terminal 1
cd sdk/typescript
npx ts-node subscriber.ts

# Terminal 2
cd sdk/typescript
npx ts-node publisher.ts

Single-File Example (Intra-Process)

For a quick test without networking, you can publish and subscribe in the same process using INTRA_PROCESS transport:

import { Participant, QoS, WaitSet, TransportMode } from "@hdds/client";

interface HelloMessage {
message: string;
count: number;
timestamp: number;
}

const participant = Participant.create("ts_hello_world", {
transport: TransportMode.INTRA_PROCESS,
});

const qos = QoS.reliable().transientLocal().historyDepth(10);
const writer = participant.createWriter<HelloMessage>("hello_world", qos);
const reader = participant.createReader<HelloMessage>("hello_world", qos.clone());

const waitset = new WaitSet();
waitset.attachReader(reader);

for (let i = 1; i <= 5; i++) {
writer.writeJson({ message: "Hello from TypeScript!", count: i, timestamp: Date.now() });

if (waitset.wait(1.0)) {
const received = reader.takeJson<HelloMessage>();
if (received !== null) {
console.log(`${received.message} (count: ${received.count})`);
}
}
}

waitset.dispose();
qos.dispose();
participant.dispose();

QoS Configuration

The SDK provides a fluent builder API for QoS:

import { QoS } from "@hdds/client";

// Reliable with deadline and partitioning
const qos = QoS.reliable()
.transientLocal()
.historyDepth(10)
.deadlineMs(500)
.partition("sensor_data")
.livelinessAutomatic(5.0);

// Inspect QoS settings
console.log(qos.isReliable()); // true
console.log(qos.getHistoryDepth()); // 10
console.log(qos.toString()); // "QoS(reliable, transient_local, depth=10)"

// Clone for reuse (writer and reader need separate instances)
const readerQos = qos.clone();

// RTI Connext compatible defaults
const rtiQos = QoS.rtiDefaults();

Error Handling

All operations throw HddsError on failure:

import { HddsError, HddsErrorCode, Participant } from "@hdds/client";

try {
const p = Participant.create("test");
} catch (e) {
if (e instanceof HddsError) {
console.error(`HDDS error [${e.code}]: ${e.message}`);
}
}

Architecture

Node.js Application
|
| @hdds/client (koffi FFI)
v
+----------------+
| libhdds_c | Native shared library
+----------------+
|
| UDP Multicast / RTPS
v
+----------------+
| DDS Domain | Other DDS peers (any vendor)
+----------------+

The SDK loads the hdds-c shared library at runtime and calls C functions directly. No intermediate bridge process is needed.

Browser support

The native FFI SDK is Node.js only. For browser applications, use the hdds-ws WebSocket bridge instead.

What's Next?