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

Getting Started with LatteStream

Build your first real-time application in under 5 minutes

Welcome to LatteStream! This guide will take you from zero to a working real-time application with our powerful WebSocket infrastructure. By the end, you'll have a complete understanding of channels, events, and authentication.

What You'll Build

In this guide, you'll create:

  • A real-time chat application
  • Message broadcasting across multiple clients
  • Private channel authentication
  • Connection state management

Prerequisites

  • Node.js 18+ installed
  • Basic knowledge of JavaScript/TypeScript
  • A code editor (VSCode recommended)

Quick Example

Here's a simple example to get you started:

import LatteStream from '@lattestream/client'; const client = new LatteStream('lspk_PUBLIC_KEY', { cluster: 'eu1', authEndpoint: '/auth', }); // Connect to a channel const channel = client.subscribe('private-chat-room'); // Listen for messages channel.bind('client-message', (data) => { console.log('New message:', data); }); // Send a message channel.trigger('client-message', { user: 'John', text: 'Hello, World!', });

You can also use TypeScript for better type safety:

interface Message { user: string; text: string; timestamp: Date; } const channel = client.subscribe<Message>('private-chat-room'); channel.bind('client-message', (data: Message) => { console.log(`${data.user}: ${data.text}`); });

And here's how to set up your HTML:

<!DOCTYPE html> <html> <head> <title>LatteStream Chat</title> </head> <body> <div id="messages"></div> <input type="text" id="messageInput" placeholder="Type a message..." /> <button onclick="sendMessage()">Send</button> <script src="https://cdn.jsdelivr.net/npm/@lattestream/client@latest/dist/index.js"></script> <script> // Your LatteStream code here </script> </body> </html>
  • 5 minutes of your time

Step 1: Create Your Account

  1. Sign up at lattestream.com/signup
  2. Verify your email by clicking the verification link
  3. Setup MFA optional, HIGHLY recommended
  4. Create your first application in the dashboard
  5. Copy your credentials:
    • Public Key: Used by client-side code (safe to expose)
    • Private Key: Used by server-side code (keep secret)
# Your credentials will look like this: Public Key: lspk_1a2b3c4d5e6f7g8h... Private Key: lsk_9z8y7x6w5v4u3t2s...

Step 2: Install the SDK

LatteStream provides official SDKs for multiple languages. We'll use JavaScript for this guide:

# Install the client SDK npm install @lattestream/client # Install the server SDK (for backend) npm install @lattestream/server

Step 3: Your First Connection

Let's start with a simple HTML page that connects to LatteStream:

<!DOCTYPE html> <html> <head> <title>My First LatteStream App</title> <style> body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; } #status { padding: 10px; border-radius: 5px; margin: 10px 0; } .connected { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; } .disconnected { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; } #messages { border: 1px solid #ddd; height: 300px; overflow-y: scroll; padding: 10px; margin: 10px 0; } #messageInput { width: 70%; padding: 10px; } #sendButton { padding: 10px 20px; } </style> </head> <body> <h1>LatteStream Real-time Chat</h1> <div id="status" class="disconnected">Connecting...</div> <div id="messages"></div> <div> <input type="text" id="messageInput" placeholder="Type a message..." /> <button id="sendButton" disabled>Send</button> </div> <script src="https://cdn.jsdelivr.net/npm/@lattestream/client@latest/dist/index.js"></script> <script> // Initialize LatteStream with your App Key const lattestream = new LatteStream('lspk_yourpublickey', { authEndpoint: '/auth', // We'll set this up later }); const statusDiv = document.getElementById('status'); const messagesDiv = document.getElementById('messages'); const messageInput = document.getElementById('messageInput'); const sendButton = document.getElementById('sendButton'); // Connection state handlers lattestream.connection.bind('connected', () => { statusDiv.textContent = 'Connected! ✅'; statusDiv.className = 'connected'; sendButton.disabled = false; console.log('Connected with socket ID:', lattestream.getSocketId()); }); lattestream.connection.bind('disconnected', () => { statusDiv.textContent = 'Disconnected ❌'; statusDiv.className = 'disconnected'; sendButton.disabled = true; }); lattestream.connection.bind('error', (error) => { statusDiv.textContent = `Error: ${error.message}`; statusDiv.className = 'disconnected'; }); // Connect to LatteStream lattestream.connect(); // We'll add channel subscription in the next step </script> </body> </html>

Remember to replace lspk_yourpublickey with your actual Public Key!

Step 4: Subscribe to Channels

Now let's add real-time messaging by subscribing to a channel:

// Subscribe to a public channel const channel = lattestream.subscribe('private-chat-room'); // Handle successful subscription channel.bind('lattestream:subscription_succeeded', () => { console.log('Successfully subscribed to chat-room'); addMessage('System', 'Connected to chat room!', 'system'); }); // Listen for new messages channel.bind('new-message', (data) => { addMessage(data.username, data.message, 'received'); }); // Function to display messages function addMessage(username, message, type) { const messageElement = document.createElement('div'); messageElement.style.margin = '5px 0'; messageElement.style.padding = '8px'; messageElement.style.borderRadius = '5px'; if (type === 'system') { messageElement.style.background = '#e3f2fd'; messageElement.style.color = '#1565c0'; } else if (type === 'sent') { messageElement.style.background = '#e8f5e8'; messageElement.style.textAlign = 'right'; } else { messageElement.style.background = '#f5f5f5'; } const timestamp = new Date().toLocaleTimeString(); messageElement.innerHTML = `<strong>${username}</strong> <span style="color: #666; font-size: 0.8em;">${timestamp}</span><br>${message}`; messagesDiv.appendChild(messageElement); messagesDiv.scrollTop = messagesDiv.scrollHeight; } // Handle sending messages function sendMessage() { const message = messageInput.value.trim(); if (!message) return; // For now, we'll just show the message locally // We'll add server-side triggering in the next step addMessage('You', message, 'sent'); messageInput.value = ''; } // Event listeners sendButton.addEventListener('click', sendMessage); messageInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { sendMessage(); } });

Step 5: Server-Side Message Broadcasting

To send messages to all connected clients, you need a server. Here's a simple Node.js server:

// server.js const express = require('express'); const { LatteStreamServer } = require('@lattestream/server'); const path = require('path'); const app = express(); app.use(express.json()); app.use(express.static('public')); // Serve your HTML file from 'public' directory // Initialize LatteStream server const lattestream = new LatteStreamServer('lsk_YOUR_PRIVATE_KEY_HERE'); // API endpoint to send messages app.post('/api/send-message', async (req, res) => { const { username, message } = req.body; try { // Broadcast message to all clients on the channel await lattestream.trigger('private-chat-room', 'new-message', { username, message, timestamp: new Date().toISOString(), }); res.json({ success: true }); } catch (error) { console.error('Error sending message:', error); res.status(500).json({ error: 'Failed to send message' }); } }); const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server running on http://localhost:${PORT}`); });

Update your client-side sendMessage function:

async function sendMessage() { const message = messageInput.value.trim(); if (!message) return; try { // Send to server to broadcast to all clients const response = await fetch('/api/send-message', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: 'Anonymous', // You can add username input later message: message, }), }); if (response.ok) { messageInput.value = ''; } else { console.error('Failed to send message'); } } catch (error) { console.error('Error sending message:', error); } }

Step 6: Add User Authentication

For private channels and user identification, you'll need authentication:

// server.js - Add authentication endpoint app.post('/auth', (req, res) => { const { socket_id, channel_name } = req.body; // In production, verify the user's identity here const userId = 'user-' + Math.random().toString(36).substr(2, 9); // Generate authorization for private channels const auth = lattestream.authorizeChannel(socket_id, channel_name, { user_id: userId, user_info: { name: 'Anonymous User', }, }); res.json(auth); });

Update your client to use a private channel:

// Change from public to private channel const channel = lattestream.subscribe('private-chat-room');

Step 7: Complete React Chat Application

Here's a complete React chat application based on our working example:

# Create a new React project npx create-react-app lattestream-chat --template typescript cd lattestream-chat # Install LatteStream npm install @lattestream/client

App.tsx:

import React, { useState } from 'react'; import { Chat } from './components/Chat'; import { UsernameForm } from './components/UsernameForm'; import './App.css'; function App() { const [username, setUsername] = useState<string>(''); return ( <div className="App"> {username ? ( <Chat username={username} /> ) : ( <UsernameForm onSubmit={setUsername} /> )} </div> ); } export default App;

hooks/useLatteStreamChat.ts:

import { useState, useEffect, useCallback } from 'react'; import { LatteStream, Channel } from '@lattestream/client'; interface Message { id: string; userId: string; username: string; text: string; timestamp: Date; isOwn?: boolean; } export const useLatteStreamChat = (username: string) => { const [messages, setMessages] = useState<Message[]>([]); const [connectionState, setConnectionState] = useState<string>('connecting'); const [currentUser, setCurrentUser] = useState<any>(null); useEffect(() => { // Initialize LatteStream const lattestream = new LatteStream('lspk_PUBLIC_KEY_HERE', { authEndpoint: 'http://localhost:3030/auth', }); // Connection handlers lattestream.connection.bind('connected', () => { setConnectionState('connected'); setCurrentUser({ id: `user-${Math.random().toString(36).substring(2, 11)}`, name: username, socketId: lattestream.getSocketId(), }); }); lattestream.connection.bind('disconnected', () => { setConnectionState('disconnected'); }); // Connect lattestream.connect(); // Subscribe to channel const channel = lattestream.subscribe('private-chat-room'); // Message handler channel.bind('new-message', (data: any) => { const message: Message = { id: data.id || Date.now().toString(), userId: data.userId, username: data.username, text: data.text, timestamp: new Date(data.timestamp), }; setMessages((prev) => [...prev, message]); }); return () => { lattestream.disconnect(); }; }, [username]); const sendMessage = useCallback( async (text: string) => { if (!currentUser) return; const message: Message = { id: Date.now().toString(), userId: currentUser.id, username: currentUser.name, text, timestamp: new Date(), isOwn: true, }; // Add to local messages immediately setMessages((prev) => [...prev, message]); // Send to server try { await fetch('/api/send-message', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: message.id, userId: message.userId, username: message.username, text: message.text, timestamp: message.timestamp.toISOString(), }), }); } catch (error) { console.error('Failed to send message:', error); } }, [currentUser] ); return { messages, connectionState, currentUser, sendMessage, }; };

Step 8: Advanced Features

Typing Indicators

// Client-side typing indicator let typingTimer; messageInput.addEventListener('input', () => { // Clear existing timer clearTimeout(typingTimer); // Send typing event channel.trigger('client-typing', { username: 'Anonymous', }); // Stop typing after 1 second of inactivity typingTimer = setTimeout(() => { channel.trigger('client-stopped-typing', { username: 'Anonymous', }); }, 1000); }); // Listen for typing events channel.bind('client-typing', (data) => { showTypingIndicator(data.username); });

Message History

// Server-side: Store messages in database const messages = []; app.post('/api/send-message', async (req, res) => { const message = { id: Date.now().toString(), ...req.body, timestamp: new Date().toISOString(), }; // Store in database (example with in-memory array) messages.push(message); // Broadcast to all clients await lattestream.trigger('chat-room', 'new-message', message); res.json({ success: true }); }); // Get message history app.get('/api/messages', (req, res) => { res.json(messages.slice(-50)); // Return last 50 messages });

Congratulations!

You've successfully built a complete real-time chat application with LatteStream! Here's what you've accomplished:

  • Connected to LatteStream from a web browser
  • Implemented real-time messaging with channels
  • Added server-side message broadcasting
  • Set up authentication for private channels
  • Built a complete React chat application
  • Added advanced features like typing indicators

What's Next?

Now that you have the basics working, explore these advanced topics:

Production Deployment

Scaling Your Application

Advanced Features

Framework Integrations

Troubleshooting

Common Issues

Connection Failed

// Check your App Key and cluster const lattestream = new LatteStream('your-app-key', { cluster: 'eu1', // Make sure you're using the correct cluster enableLogging: true, // Enable logging for debugging });

Authentication Errors

// Verify your auth endpoint returns the correct format { "auth": "app-key:signature", "channel_data": "{\"user_id\":\"123\",\"user_info\":{\"name\":\"John\"}}" }

Messages Not Appearing

  • Check that both client and server use the same channel name
  • Verify your Master Key is correct in server code
  • Check browser console for JavaScript errors

Getting Help


Ready for more? Check out our complete examples or dive deeper into the JavaScript SDK documentation.