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]);
Tendrl