LatteStreamยฎ

Quick Start

Getting Started

Building Custom SDKs

SDKs & Libraries

JavaScript/TypeScript

Node.js / Bun / Deno

Python

In Development

Go

In Development

PHP

In Development

API Reference

WebSocket API

REST API

Webhooks

Authentication

JavaScript/TypeScript SDK

The official LatteStream SDK for JavaScript and TypeScript applications

The LatteStream JavaScript SDK provides a powerful, type-safe way to add real-time messaging to any frontend application. Built with TypeScript-first design and Pusher-compatible APIs for easy migration.

Quick Start

Installation

npm install @lattestream/client # or yarn add @lattestream/client # or pnpm add @lattestream/client

Basic Usage

import LatteStream from '@lattestream/client'; // Initialize client const lattestream = new LatteStream('your-app-key', { cluster: 'us-east-1', authEndpoint: '/auth', }); // Connect lattestream.connect(); // Subscribe to a channel const channel = lattestream.subscribe('my-channel'); // Listen for events channel.bind('my-event', (data) => { console.log('Received:', data); });

Features

  • TypeScript First - Complete type safety with full TypeScript definitions
  • Auto-reconnection - Robust connection handling with exponential backoff
  • Framework Agnostic - Works with React, Vue, Angular, or vanilla JavaScript
  • Secure Authentication - Built-in support for private and presence channels
  • High Performance - Optimized for minimal bundle size and fast connections
  • Pusher Compatible - Familiar API for easy migration from Pusher

Browser Support

  • Chrome 60+
  • Firefox 55+
  • Safari 11+
  • Edge 79+
  • iOS Safari 11+
  • Android Chrome 60+

Core Concepts

Connection States

The LatteStream client manages connection states automatically:

lattestream.connection.bind('connecting', () => { console.log('Connecting to LatteStream...'); }); lattestream.connection.bind('connected', () => { console.log('Connected! Socket ID:', lattestream.getSocketId()); }); lattestream.connection.bind('disconnected', () => { console.log('Disconnected from LatteStream'); }); lattestream.connection.bind('reconnecting', () => { console.log('Attempting to reconnect...'); }); lattestream.connection.bind('failed', () => { console.log('Connection failed'); });

Channel Types

Public Channels

No authentication required. Perfect for broadcasting public updates:

const publicChannel = lattestream.subscribe('news-updates'); publicChannel.bind('breaking-news', (data) => { showNotification(data.title, data.summary); });

Private Channels

Require authentication. Channel names must start with private-:

const privateChannel = lattestream.subscribe('private-user-123'); privateChannel.bind('personal-message', (data) => { displayPersonalMessage(data); });

Presence Channels

Track who's online. Channel names must start with presence-:

const presenceChannel = lattestream.subscribe('presence-chat-room'); // When someone joins presenceChannel.bind('lattestream:member_added', (member) => { console.log(`${member.info.name} joined the chat`); updateUserList(); }); // When someone leaves presenceChannel.bind('lattestream:member_removed', (member) => { console.log(`${member.info.name} left the chat`); updateUserList(); }); // Get all current members const members = presenceChannel.getMembers(); console.log('Current members:', members);

Event Binding

Bind to events at the global or channel level:

// Global events (across all channels) lattestream.bind('global-announcement', (data) => { showGlobalAlert(data.message); }); // Channel-specific events channel.bind('user-typing', (data) => { showTypingIndicator(data.userId); }); // Multiple event handlers channel.bind('message', handleMessage); channel.bind('message', logMessage); channel.bind('message', updateUnreadCount); // Unbind specific handlers channel.unbind('message', logMessage); // Unbind all handlers for an event channel.unbind('message');

Configuration Options

Client Options

interface LatteStreamOptions { // Connection settings wsEndpoint?: string; // Custom WebSocket endpoint cluster?: string; // Geographic cluster (us-east-1, eu-west-1, etc.) forceTLS?: boolean; // Force secure connections (default: true) // Authentication authEndpoint?: string; // Endpoint for channel authorization authTransport?: 'ajax' | 'jsonp'; // Authorization transport method auth?: { headers?: Record<string, string>; // Custom auth headers params?: Record<string, string>; // Custom auth parameters }; // Connection behavior enableLogging?: boolean; // Enable debug logging activityTimeout?: number; // Activity timeout in ms (default: 120000) pongTimeout?: number; // Pong timeout in ms (default: 30000) maxReconnectionAttempts?: number; // Max reconnection attempts (default: 6) maxReconnectGapInSeconds?: number; // Max delay between reconnects (default: 30) // Performance enableStats?: boolean; // Enable connection statistics disableStats?: boolean; // Disable connection statistics (deprecated) }

Example with Custom Configuration

const lattestream = new LatteStream('your-app-key', { cluster: 'eu-west-1', enableLogging: true, activityTimeout: 60000, maxReconnectionAttempts: 10, authEndpoint: 'https://yourapi.com/lattestream/auth', auth: { headers: { Authorization: 'Bearer ' + userToken, }, params: { user_id: currentUser.id, }, }, });

Authentication

Setting Up Authentication

For private and presence channels, you need an authentication endpoint:

// Client configuration const lattestream = new LatteStream('your-app-key', { authEndpoint: '/lattestream/auth', }); // Your server endpoint should return: // { // auth: "app-key:signature", // channel_data: "{\"user_id\":\"123\",\"user_info\":{\"name\":\"John\"}}" // for presence channels // }

Custom Authorization

You can also provide a custom authorizer function:

const lattestream = new LatteStream('your-app-key', { authorizer: (channel, options) => { return { authorize: (socketId, callback) => { // Your custom authorization logic fetch('/custom-auth', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ socket_id: socketId, channel_name: channel.name, }), }) .then((response) => response.json()) .then((data) => callback(null, data)) .catch((error) => callback(error, null)); }, }; }, });

Performance & Optimization

Connection Pooling

Reuse connections across your application:

// Create a single instance const lattestream = new LatteStream('your-app-key'); // Export for use across components export default lattestream; // In other files import lattestream from './lattestream-client'; const channel = lattestream.subscribe('my-channel');

Lazy Channel Subscription

Subscribe to channels only when needed:

class ChatManager { constructor(lattestream) { this.lattestream = lattestream; this.channels = new Map(); } joinRoom(roomId) { if (!this.channels.has(roomId)) { const channel = this.lattestream.subscribe(`presence-room-${roomId}`); this.channels.set(roomId, channel); channel.bind('message', this.handleMessage.bind(this)); } return this.channels.get(roomId); } leaveRoom(roomId) { const channel = this.channels.get(roomId); if (channel) { this.lattestream.unsubscribe(`presence-room-${roomId}`); this.channels.delete(roomId); } } }

Bundle Size Optimization

Import only what you need:

// Tree-shaking friendly imports import { LatteStream } from '@lattestream/client/core'; import { PresenceChannel } from '@lattestream/client/presence'; // Or use dynamic imports for code splitting async function loadLatteStream() { const { default: LatteStream } = await import('@lattestream/client'); return new LatteStream('your-app-key'); }

Error Handling

Connection Errors

lattestream.connection.bind('error', (error) => { console.error('Connection error:', error); // Handle specific error types switch (error.type) { case 'WebSocketError': showErrorMessage( 'Connection failed. Please check your internet connection.' ); break; case 'AuthError': showErrorMessage('Authentication failed. Please log in again.'); break; case 'LimitExceeded': showErrorMessage('Connection limit exceeded. Please try again later.'); break; default: showErrorMessage('An unexpected error occurred.'); } });

Subscription Errors

channel.bind('lattestream:subscription_error', (error) => { console.error('Subscription error:', error); if (error.status === 403) { showErrorMessage('You do not have permission to access this channel.'); } else if (error.status === 401) { showErrorMessage('Authentication required. Please log in.'); } });

Graceful Degradation

const lattestream = new LatteStream('your-app-key', { // Fallback options when WebSocket fails fallbackTransports: ['xhr_polling', 'xhr_streaming'], }); // Detect connection quality lattestream.connection.bind('state_change', (states) => { if (states.current === 'connected') { hideOfflineIndicator(); } else { showOfflineIndicator(); // Switch to polling mode for better reliability enablePollingMode(); } });

๐Ÿงช Testing

Mocking LatteStream

// test-utils.js export const mockLatteStream = { connect: jest.fn(), disconnect: jest.fn(), subscribe: jest.fn(() => ({ bind: jest.fn(), unbind: jest.fn(), trigger: jest.fn(), })), unsubscribe: jest.fn(), getSocketId: jest.fn(() => 'mock-socket-id'), }; // In your tests import { mockLatteStream } from './test-utils'; jest.mock('@lattestream/client', () => ({ default: jest.fn(() => mockLatteStream), }));

Integration Testing

// integration-test.js import LatteStream from '@lattestream/client'; describe('LatteStream Integration', () => { let lattestream; beforeAll(async () => { lattestream = new LatteStream(process.env.TEST_APP_KEY); await new Promise((resolve) => { lattestream.connection.bind('connected', resolve); lattestream.connect(); }); }); test('should receive messages on subscribed channel', (done) => { const channel = lattestream.subscribe('test-channel'); channel.bind('test-event', (data) => { expect(data.message).toBe('Hello Test'); done(); }); // Trigger event from server (separate test setup) triggerTestEvent('test-channel', 'test-event', { message: 'Hello Test' }); }); });

API Reference

LatteStream Class

Constructor

new LatteStream(appKey: string, options?: LatteStreamOptions)

Methods

  • connect(): void - Connect to LatteStream
  • disconnect(): void - Disconnect from LatteStream
  • subscribe(channelName: string): Channel - Subscribe to a channel
  • unsubscribe(channelName: string): void - Unsubscribe from a channel
  • bind(eventName: string, callback: Function): void - Listen for global events
  • unbind(eventName: string, callback?: Function): void - Stop listening for global events
  • getSocketId(): string | null - Get current socket ID
  • getConnectionState(): string - Get connection state

Properties

  • connection: Connection - Connection object with state and events
  • channels: Channels - Channel collection object

Channel Class

Methods

  • bind(eventName: string, callback: Function): Channel - Listen for channel events
  • unbind(eventName: string, callback?: Function): Channel - Stop listening for events
  • trigger(eventName: string, data: any): boolean - Trigger client event (private/presence only)

Properties

  • name: string - Channel name
  • subscribed: boolean - Subscription status

PresenceChannel Class

Extends Channel with additional presence functionality:

Methods

  • getMembers(): Members - Get all online members
  • getMember(id: string): Member | null - Get specific member
  • getMyId(): string | null - Get your user ID
  • getMemberCount(): number - Get member count

Related Documentation

Support


Next Steps: Check out the Server SDK documentation to learn how to send messages from your backend.