Docs / Contact / sdks/javascript/examples

JavaScript SDK: Examples

Practical examples for common use cases with the Tendrl JavaScript SDK.

React Dashboard#

A complete React component that displays connection status and sends/receives messages:

javascript


function DeviceDashboard() {
    const [messages, setMessages] = useState([]);
    const [sensorData, setSensorData] = useState({ temperature: 0, humidity: 0 });

    const [online, setOnline] = useState(false);

    // The API key comes from REACT_APP_TENDRL_KEY; the hook takes no apiKey option.
    const { client, publish } = useTendrlClient({
        onMessage: (msg) => {
            setMessages(prev => [...prev.slice(-49), msg]); // Keep last 50
        },
        offlineStorage: true,
        checkMsgRate: 3000,
        checkMsgLimit: 10
    });

    // The hook's own isConnected is a render-time snapshot and never re-renders,
    // so poll for status and keep it in state instead.
    useEffect(() => {
        if (!client) return;
        let canceled = false;
        const poll = async () => {
            const up = await client.checkConnectionState();
            if (!canceled) setOnline(up);
        };
        poll();
        const id = setInterval(poll, 5000);
        return () => { canceled = true; clearInterval(id); };
    }, [client]);

    const sendReading = () => {
        publish(sensorData, ['sensor', 'dashboard']);
    };

    return (
        <div>
            <h2>Device Dashboard</h2>
            <p>Status: {online ? 'Online' : 'Offline'}</p>

            <div>
                <input
                    type="number"
                    value={sensorData.temperature}
                    onChange={e => setSensorData(prev => ({
                        ...prev,
                        temperature: parseFloat(e.target.value)
                    }))}
                    placeholder="Temperature"
                />
                <button onClick={sendReading}>Send</button>

            <h3>Messages ({messages.length})</h3>
            <ul>
                {messages.map((msg, i) => (
                    <li key={i}>
                        [{msg.msg_type}] {JSON.stringify(msg.data)}
                    </li>
                ))}
            </ul>
    );
}

Node.js Service#

A Node.js service that collects and forwards data:

javascript


const client = new TendrlClient({
    apiKey: process.env.TENDRL_KEY,
    offlineStorage: false, // IndexedDB not available in Node
    debug: true
});

// One callback receives everything; branch on the tags yourself.
// The SDK moves the server's tags into message.context.tags.
client.setMessageCallback((message) => {
    const { msg_type, data, source } = message;
    const tags = message.context?.tags || [];
    if (tags.includes('ai-response')) {
        console.log(`[${msg_type}] from ${source}:`, data);
    } else if (tags.includes('alert') || tags.includes('anomaly')) {
        console.log('Alert:', data);
    }
});
client.setMessageCheckRate(5000);

client.start();

// Periodic metrics
setInterval(() => {
    const mem = process.memoryUsage();
    client.publish({
        heap_used: mem.heapUsed,
        heap_total: mem.heapTotal,
        rss: mem.rss,
        uptime: process.uptime()
    }, ['metrics', 'node']);
}, 30000);

process.on('SIGINT', () => {
    client.stop();
    process.exit();
});

State Table Management#

The JavaScript SDK has no state-table methods — no getState, updateState, replaceState or onState. Use fetch against the REST endpoints:

javascript

const apiKey = process.env.TENDRL_KEY;
const base = `${process.env.TENDRL_APP_URL || 'https://app.tendrl.com'}/api/entities/status-table`;
const headers = {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
};

// Read current state
const state = await (await fetch(base, { headers })).json();
console.log('Current state:', state);

// Merge new data (PATCH; existing keys preserved)
await fetch(base, {
    method: 'PATCH',
    headers,
    body: JSON.stringify({
        firmware: '2.1.0',
        lastSeen: new Date().toISOString(),
    }),
});

// Replace entire state (PUT; removes all previous keys)
await fetch(base, {
    method: 'PUT',
    headers,
    body: JSON.stringify({ firmware: '2.1.0', status: 'active' }),
});

Heartbeat#

Send system information to Contact:

javascript

await client.sendHeartbeat({
    mem_free: 8589934592,
    mem_total: 17179869184,
    disk_free: 107374182400,
    disk_size: 1073741824000
});

Data Types#

publish() takes an object (arrays included) or a string. A bare number throws Invalid message type: number. Expected string or object.

javascript

// Object (most common)
client.publish({ key: 'value' }, ['tag']);

// String (auto-wrapped as {data: "..."})
client.publish('simple message', ['tag']);

// Array
client.publish([1, 2, 3], ['tag']);

// Nested structures
client.publish({
    sensors: [
        { id: 'temp-1', value: 23.5 },
        { id: 'temp-2', value: 24.1 }
    ],
    timestamp: new Date().toISOString()
}, ['sensor', 'batch']);

Connection Monitoring#

javascript

// Check connection state. The result is cached for 30 seconds, so calling this
// in a tight loop returns the same answer without hitting the network again.
const online = await client.checkConnectionState();
console.log('Connected:', online);

In React, the hook's isConnected is a render-time snapshot rather than reactive state, so it never re-renders on a connectivity change. Poll and store it yourself:

javascript

const [online, setOnline] = useState(false);

useEffect(() => {
    if (!client) return;
    const id = setInterval(async () => {
        setOnline(await client.checkConnectionState());
    }, 5000);
    return () => clearInterval(id);
}, [client]);