JavaScript SDK
The official NoLag SDK for JavaScript and TypeScript. Full TypeScript support with comprehensive type definitions.
Installation
# npm
npm install @nolag/js-sdk
# yarn
yarn add @nolag/js-sdk
# pnpm
pnpm add @nolag/js-sdkQuick Start
import { NoLag } from '@nolag/js-sdk'
// Create client with access token
const client = NoLag('your-access-token')
// Connect to NoLag
await client.connect()
// Subscribe to a topic
client.subscribe('chat/room-1')
// Listen for messages
client.on('chat/room-1', (data, meta) => {
console.log('Received:', data)
})
// Publish a message
client.emit('chat/room-1', { text: 'Hello!' })Connection Options
const client = NoLag('your-access-token', {
// Connection
url: 'wss://broker.nolag.app/ws', // WebSocket URL (default)
reconnect: true, // Auto-reconnect (default: true)
reconnectInterval: 5000, // Initial reconnect delay ms (default: 5000)
maxReconnectAttempts: 10, // Max reconnection attempts (default: 10)
// Messaging
qos: 1, // Default QoS level: 0, 1, or 2 (default: 1)
// Load Balancing
loadBalance: false, // Enable load balancing (default: false)
loadBalanceGroup: 'worker-pool', // Load balance group name
// Browser-specific
disconnectOnHidden: false, // Disconnect when tab hidden (default: false)
heartbeatInterval: 30000, // Heartbeat interval ms (default: 30000)
// Debugging
debug: false, // Enable debug logging (default: false)
})Connection Management
// Connect to NoLag
await client.connect()
// Check connection status
console.log(client.status) // 'disconnected' | 'connecting' | 'connected' | 'reconnecting'
console.log(client.connected) // true or false
// Disconnect (prevents auto-reconnect)
client.disconnect()
// Access client info (available after connect)
console.log(client.actorId) // Your actor token ID
console.log(client.actorType) // 'device' | 'user' | 'server'
console.log(client.projectId) // Project IDClient Events
// Connection established
client.on('connect', () => {
console.log('Connected to NoLag')
})
// Connection lost
client.on('disconnect', (reason) => {
console.log('Disconnected:', reason)
})
// Reconnection starting
client.on('reconnect', () => {
console.log('Attempting to reconnect...')
})
// Error occurred
client.on('error', (error) => {
console.error('Error:', error)
})Subscribing to Topics
// Basic subscription
client.subscribe('chat/room-1')
// With options
client.subscribe('chat/room-1', {
qos: 2, // Override default QoS
loadBalance: true, // Enable load balancing for this topic
loadBalanceGroup: 'workers', // Specific load balance group
})
// With acknowledgment callback
client.subscribe('chat/room-1', (error) => {
if (error) {
console.error('Subscribe failed:', error)
} else {
console.log('Subscribed successfully')
}
})
// Wildcard subscriptions
client.subscribe('chat/+/messages') // Single level (+)
client.subscribe('users/123/#') // Multi level (#)Listening for Messages
// Listen to a specific topic
client.on('chat/room-1', (data, meta) => {
console.log('Data:', data)
console.log('From:', meta.from) // Sender's actorTokenId
console.log('Time:', meta.timestamp) // Server timestamp
})
// Listen to all subscribed topics (wildcard handler)
client.onAny((topic, data, meta) => {
console.log(`[${topic}]`, data)
})
// Remove a specific handler
const handler = (data) => console.log(data)
client.on('chat/room-1', handler)
client.off('chat/room-1', handler)
// Remove all handlers for a topic
client.off('chat/room-1')Publishing Messages
// Basic publish
client.emit('chat/room-1', { text: 'Hello!' })
// With options
client.emit('chat/room-1', { text: 'Important' }, {
qos: 2, // Override default QoS
retain: true, // Retain message for new subscribers
echo: false, // Don't receive your own message (default: true)
})
// With acknowledgment callback
client.emit('chat/room-1', { text: 'Hello' }, (error) => {
if (error) {
console.error('Publish failed:', error)
} else {
console.log('Message sent')
}
})
// With options and callback
client.emit('chat/room-1', { text: 'Hello' }, { qos: 2 }, (error) => {
if (error) console.error(error)
})Room Presence
Presence is scoped to rooms. Use the fluent API to set and observe presence:
// Join a room and set presence
const room = client.setApp('chat').setRoom('general')
room.setPresence({
username: 'Alice',
status: 'online',
avatar: '/img/alice.png'
})
// Get all actors in this room (from local cache)
const actors = room.getPresence()
console.log('In room:', Object.keys(actors).length)
// Fetch presence list from server (async)
const freshList = await room.fetchPresence()
// Listen for presence events in this room
room.on('presence:join', (actor) => {
console.log(`${actor.presence.username} joined`)
})
room.on('presence:leave', (actor) => {
console.log(`${actor.presence.username} left`)
})
room.on('presence:update', (actor) => {
console.log(`${actor.presence.username} updated status`)
})Lobbies (Multi-Room Presence)
Lobbies let you observe presence across multiple rooms. Use them for dashboards and monitoring interfaces:
// Subscribe to a lobby to observe all rooms
const lobby = client.setApp('rides').setLobby('active-trips')
// Subscribe returns a snapshot of current presence
const snapshot = await lobby.subscribe()
// snapshot: { roomId -> { actorId -> presenceData } }
console.log('Active trips:', Object.keys(snapshot).length)
// Listen for presence events (includes room context)
lobby.on('presence:join', (event) => {
// event: { lobbyId, roomId, actorId, data }
console.log(`${event.actorId} joined room ${event.roomId}`)
addToMap(event.roomId, event.actorId, event.data)
})
lobby.on('presence:update', (event) => {
updateOnMap(event.roomId, event.actorId, event.data)
})
lobby.on('presence:leave', (event) => {
removeFromMap(event.roomId, event.actorId)
})
// Fetch fresh presence at any time
const freshPresence = await lobby.fetchPresence()
// Unsubscribe when done
lobby.unsubscribe()Read-only: Lobbies are for observation only. Actors set presence on rooms, not lobbies. See Lobbies documentation for details.
Fluent API (Scoped Pub/Sub)
Use the fluent API to scope subscriptions and messages to a specific app and room:
// Create a room context
const room = client.setApp('chat').setRoom('general')
// Subscribe (topic is auto-prefixed to 'chat/general/messages')
room.subscribe('messages')
// Listen for messages
room.on('messages', (data, meta) => {
console.log('Message:', data)
})
// Publish (also auto-prefixed)
room.emit('messages', { text: 'Hello room!' })
// Get the full topic prefix
console.log(room.prefix) // 'chat/general'
// Unsubscribe
room.unsubscribe('messages')Unsubscribing
// Unsubscribe from a topic
client.unsubscribe('chat/room-1')
// With callback
client.unsubscribe('chat/room-1', (error) => {
if (error) {
console.error('Unsubscribe failed:', error)
}
})Quality of Service (QoS)
// QoS 0: At most once (fire and forget)
client.emit('telemetry/data', data, { qos: 0 })
// QoS 1: At least once (guaranteed delivery, may duplicate)
client.emit('orders/new', order, { qos: 1 })
// QoS 2: Exactly once (guaranteed single delivery)
client.emit('payments/process', payment, { qos: 2 })
// Set default QoS for all messages
const client = NoLag(token, { qos: 1 })Load Balancing
Distribute messages across multiple clients in a group using round-robin:
// Enable for all subscriptions
const client = NoLag(token, {
loadBalance: true,
loadBalanceGroup: 'worker-pool-1'
})
// Or per-subscription
client.subscribe('jobs/process', {
loadBalance: true,
loadBalanceGroup: 'job-workers'
})
// Only ONE client in the group receives each messageREST API Client
Manage apps, rooms, and actors programmatically:
import { NoLagApi } from '@nolag/js-sdk'
const api = new NoLagApi('your-api-key', {
baseUrl: 'https://api.nolag.app/v1', // Optional
timeout: 30000, // Optional
})
// Apps
const apps = await api.apps.list()
const app = await api.apps.create({ name: 'My App' })
await api.apps.update(app.appId, { name: 'Updated Name' })
await api.apps.delete(app.appId)
// Rooms
const rooms = await api.rooms.list(appId)
const room = await api.rooms.create(appId, { name: 'General', slug: 'general' })
await api.rooms.delete(appId, room.roomId)
// Actors
const actors = await api.actors.list()
const actor = await api.actors.create({
name: 'Device 1',
actorType: 'device'
})
console.log('Access Token:', actor.accessToken) // Save this! Only shown once
await api.actors.update(actor.actorTokenId, { name: 'Updated Device' })
await api.actors.delete(actor.actorTokenId)TypeScript Support
The SDK includes full TypeScript definitions:
import {
NoLag,
NoLagApi,
NoLagOptions,
ConnectionStatus,
MessageMeta,
ActorPresence,
PresenceData,
LobbyPresenceEvent,
LobbyPresenceState,
QoS,
SubscribeOptions,
EmitOptions,
RoomContext,
LobbyContext,
} from '@nolag/js-sdk'
// Type your message data
interface ChatMessage {
text: string
sender: string
timestamp: number
}
client.on('chat/room-1', (data: ChatMessage, meta: MessageMeta) => {
console.log(`[${data.sender}]: ${data.text}`)
})Error Handling
// Connection errors
client.on('error', (error) => {
console.error('Client error:', error.message)
})
// Subscribe/emit callbacks
client.subscribe('topic', (error) => {
if (error) {
console.error('Subscribe failed:', error.message)
}
})
client.emit('topic', data, (error) => {
if (error) {
console.error('Emit failed:', error.message)
}
})
// REST API errors
import { NoLagApiError } from '@nolag/js-sdk'
try {
await api.apps.get('invalid-id')
} catch (error) {
if (error instanceof NoLagApiError) {
console.error('API Error:', error.statusCode, error.message)
}
}Browser Usage
<script type="module">
import { NoLag } from 'https://unpkg.com/@nolag/js-sdk/dist/browser.js'
const client = NoLag('your-access-token')
await client.connect()
client.subscribe('updates')
client.on('updates', (data) => {
document.getElementById('output').textContent = JSON.stringify(data)
})
</script>Complete Example
import { NoLag, NoLagApi } from '@nolag/js-sdk'
// Create an actor via REST API (server-side)
const api = new NoLagApi('your-api-key')
const actor = await api.actors.create({
name: 'chat-client',
actorType: 'user'
})
// Connect with the actor's access token (client-side)
const client = NoLag(actor.accessToken, {
reconnect: true,
qos: 1
})
client.on('connect', () => {
console.log('Connected as:', client.actorId)
// Set presence
client.setPresence({ username: 'Alice', status: 'online' })
// Subscribe to chat room
const room = client.setApp('chat').setRoom('general')
room.subscribe('messages')
room.on('messages', (data, meta) => {
console.log(`Message from ${meta.from}:`, data)
})
})
client.on('presence:join', (actor) => {
console.log(`${actor.presence.username} joined`)
})
client.on('disconnect', (reason) => {
console.log('Disconnected:', reason)
})
client.on('error', (error) => {
console.error('Error:', error)
})
await client.connect()
// Send a message
const room = client.setApp('chat').setRoom('general')
room.emit('messages', { text: 'Hello everyone!' })