Ably Real-Time Platform: Complete Developer Cheat Sheet

Introduction to Ably

Ably is a real-time data delivery platform providing pub/sub messaging, presence, and data stream processing capabilities. It’s designed for building scalable real-time applications with guaranteed message delivery, global distribution, and high availability. Ably supports use cases like live chat, live updates, geolocation tracking, multiplayer collaboration, and IoT device communication.

Core Concepts & Principles

Key Terminology

TermDefinition
Pub/SubPublisher/Subscriber messaging pattern where publishers send messages to channels and subscribers receive them
ChannelNamed stream for publishing and subscribing to messages
MessageData packet containing payload and metadata
PresenceFeature enabling clients to announce their state on a channel
ConnectionReal-time network connection between client and Ably
ClientApplication, service or device connected to Ably
TokenShort-lived authentication credential
KeyAPI key for authentication and authorization
RESTRequest/response API for non-persistent connections
RealtimePersistent connection for instant message delivery

Protocol Support

ProtocolUse CaseCharacteristics
WebSocketsBrowser and mobile appsBi-directional, persistent connection
SSE (Server-Sent Events)One-way server-to-clientGood for updates that flow only from server to client
MQTTIoT and embedded devicesLightweight for constrained devices
STOMPLegacy and enterprise integrationText-based, enterprise messaging
RESTNon-persistent operationsRequest/response model

Ably Architecture

  • Edge Network: 300+ points of presence ensuring low-latency global connectivity
  • Regional Routing: Message routing optimized for minimal latency
  • Fallback Hosts: Automatic connection to alternate endpoints during failures
  • Stream Resumability: Automatic recovery from temporary disconnections
  • Consistent Ordering: Messages delivered in same order they were published
  • Guaranteed Delivery: Messages persisted until successfully delivered
  • Fault Tolerance: Distributed system with no single point of failure

Authentication & Authorization

Authentication Methods

MethodCharacteristicsBest For
Basic AuthenticationAPI key in client• Development<br>• Server-side applications<br>• Secure environments
Token AuthenticationShort-lived tokens• Client-side applications<br>• Browser clients<br>• Mobile applications
JWT AuthenticationJSON Web Tokens• Integration with existing auth systems<br>• Custom auth claims

Authentication Code Examples

Basic Authentication

// JavaScript
const ably = new Ably.Realtime({ key: 'your-api-key:your-api-secret' });
# Python
ably = AblyRest('your-api-key:your-api-secret')

Token Authentication

// JavaScript - Client requesting token from your server
const ably = new Ably.Realtime({ authUrl: 'https://your-server/token-auth' });

// JavaScript - Server generating token
const ably = new Ably.Rest({ key: 'your-api-key:your-api-secret' });
ably.auth.createTokenRequest({ clientId: 'client-id' }, (err, tokenRequest) => {
  // Return tokenRequest to client
});
# Python - Server generating token
ably = AblyRest('your-api-key:your-api-secret')
token_params = {'client_id': 'client-id'}
token_details = ably.auth.create_token_request(**token_params)
# Return token_details to client

Capabilities & Permissions

CapabilityDescriptionExample
subscribeReceive messages{"channel1":["subscribe"]}
publishSend messages{"channel1":["publish"]}
presenceUse presence features{"channel1":["presence"]}
historyAccess message history{"channel1":["history"]}
channel-metadataRead channel metadata{"channel1":["channel-metadata"]}
push-subscribeRegister for push notifications{"channel1":["push-subscribe"]}
push-adminManage push notifications{"channel1":["push-admin"]}

Capability Example

// JavaScript - Creating token with specific capabilities
const tokenParams = {
  capability: {
    'chat': ['publish', 'subscribe', 'presence'],
    'status': ['subscribe']
  }
};

ably.auth.createTokenRequest(tokenParams, (err, tokenRequest) => {
  // Return tokenRequest to client
});

Pub/Sub Messaging

Channel Naming

  • Namespace Pattern: {namespace}:{channel-name}
  • Private Channels: private:{channel-name}
  • Encryption: private:encrypted:{channel-name}
  • Multiple Levels: {level1}:{level2}:{level3}
  • Wildcard Subscriptions: {namespace}:* (matches all channels in namespace)

Publishing Messages

// JavaScript - Publishing a simple message
const channel = ably.channels.get('example-channel');
channel.publish('event-name', { data: 'example data' });

// JavaScript - Publishing with additional attributes
channel.publish({
  name: 'event-name',
  data: { message: 'example data' },
  extras: { metadata: 'some metadata' }
});
# Python - Publishing a message
channel = ably.channels.get('example-channel')
channel.publish('event-name', {'data': 'example data'})

Subscribing to Messages

// JavaScript - Subscribing to all messages
const channel = ably.channels.get('example-channel');
channel.subscribe((message) => {
  console.log('Received: ' + message.name + ' - ' + message.data);
});

// JavaScript - Subscribing to specific event
channel.subscribe('event-name', (message) => {
  console.log('Received: ' + message.data);
});
# Python - Subscribing to messages
def message_callback(message):
    print(f"Received: {message.name} - {message.data}")

channel = ably.channels.get('example-channel')
channel.subscribe(message_callback)

Message Structure

PropertyDescriptionExample
nameEvent name'update'
dataMessage payload{ key: 'value' }
idUnique message ID'xjkTG56:0:0'
clientIdPublisher client ID'user123'
connectionIdPublisher connection ID'hd726'
timestampPublication time2023-04-12T15:30:45.123Z
extrasAdditional metadata{ push: { notification: { title: 'Alert' } } }

Channel Options

OptionDescriptionUse Case
modesProtocol modesSpecifying connection types (realtime, presence, etc.)
paramsChannel parametersAdding metadata to channel
cipherEncryption parametersEnd-to-end encryption
channelOptionsAdditional optionsVarious channel behaviors
// JavaScript - Channel with encryption
const channelOptions = { cipher: { key: cryptoKey } };
const channel = ably.channels.get('encrypted-channel', channelOptions);

Presence

Entering/Leaving Presence

// JavaScript - Enter presence with data
const channel = ably.channels.get('example-channel');
channel.presence.enter({ status: 'online', activity: 'coding' });

// JavaScript - Update presence data
channel.presence.update({ status: 'online', activity: 'reading' });

// JavaScript - Leave presence
channel.presence.leave();
# Python - Enter presence (Note: only available in Realtime client)
channel = realtime.channels.get('example-channel')
await channel.presence.enter({'status': 'online'})

# Update presence
await channel.presence.update({'status': 'busy'})

# Leave presence
await channel.presence.leave()

Subscribing to Presence Events

// JavaScript - All presence events
channel.presence.subscribe((presenceMessage) => {
  console.log(`${presenceMessage.clientId} - ${presenceMessage.action}`);
  console.log(presenceMessage.data);
});

// JavaScript - Specific presence event
channel.presence.subscribe('enter', (presenceMessage) => {
  console.log(`${presenceMessage.clientId} entered`);
});
# Python - Subscribe to presence events
async def presence_callback(presence_message):
    print(f"{presence_message.client_id} - {presence_message.action}")

await channel.presence.subscribe(presence_callback)

Presence Message Structure

PropertyDescriptionExample
actionType of event'enter', 'update', 'leave', 'present'
clientIdClient identifier'user123'
connectionIdConnection ID'hd726'
dataPresence state data{ status: 'online', location: [51.5, -0.12] }
idUnique message ID'xjkTG56:0:0'
timestampEvent time2023-04-12T15:30:45.123Z

Getting Present Members

// JavaScript - Get all present members
channel.presence.get((err, members) => {
  members.forEach((member) => {
    console.log(`${member.clientId}: ${JSON.stringify(member.data)}`);
  });
});

// JavaScript - Get presence history
channel.presence.history((err, presenceHistory) => {
  presenceHistory.items.forEach((presenceMessage) => {
    console.log(`${presenceMessage.clientId} - ${presenceMessage.action}`);
  });
});
# Python - Get present members
members = await channel.presence.get()
for member in members:
    print(f"{member.client_id}: {member.data}")

# Python - Get presence history
history = await channel.presence.history()
for presence_message in history.items:
    print(f"{presence_message.client_id} - {presence_message.action}")

History & Storage

Channel History

// JavaScript - Get message history
channel.history((err, resultPage) => {
  resultPage.items.forEach((message) => {
    console.log(`${message.name}: ${message.data}`);
  });
});

// JavaScript - History with options
const historyOptions = {
  start: '2023-01-01T00:00:00.000Z',  // Start time
  end: '2023-01-02T00:00:00.000Z',     // End time
  direction: 'backwards',              // Order
  limit: 100                           // Max results
};

channel.history(historyOptions, (err, resultPage) => {
  // Process results
});
# Python - Get message history
history = await channel.history()
for message in history.items:
    print(f"{message.name}: {message.data}")

# Python - History with options
history_options = {
    'start': '2023-01-01T00:00:00.000Z',
    'end': '2023-01-02T00:00:00.000Z',
    'direction': 'backwards',
    'limit': 100
}
history = await channel.history(**history_options)

Persisted History

To enable message persistence (storing messages for later retrieval):

  1. Enable channel persistence in your Ably app settings
  2. Messages are stored for 24-72 hours depending on your account plan
  3. Access history as shown above

Connection & State Management

Connection States

StateDescriptionNext States
initializedInitial state before connectionconnecting
connectingAttempting to establish connectionconnected, disconnected, suspended, failed
connectedActive connection establisheddisconnected, suspended, closing, closed
disconnectedTemporary disconnectionconnecting, suspended
suspendedMultiple connection attempts failedconnecting, closed
closingConnection is closingclosed
closedConnection is closedconnecting
failedConnection failed permanently

Connection State Handling

// JavaScript - Connection state changes
ably.connection.on('connected', () => {
  console.log('Connected to Ably!');
});

ably.connection.on('failed', () => {
  console.log('Connection failed');
});

// JavaScript - Connection state recovery
const ably = new Ably.Realtime({ 
  key: 'your-api-key',
  recover: 'connection-key-to-recover' 
});
# Python - Connection state changes (with Realtime client)
async def connection_state_change(state_change):
    print(f"Connection state change: {state_change.current}")

realtime.connection.on('connected', connection_state_change)

Channel States

StateDescription
initializedInitial state before attachment
attachingIn process of attaching to channel
attachedSuccessfully attached to channel
detachingIn process of detaching from channel
detachedDetached from channel
suspendedChannel temporarily unavailable
failedChannel failed to attach

Channel State Handling

// JavaScript - Channel state changes
channel.on('attached', () => {
  console.log('Channel attached');
});

channel.on('failed', () => {
  console.log('Channel attachment failed');
});
# Python - Channel state changes
async def channel_state_change(state_change):
    print(f"Channel state change: {state_change.current}")

channel.on('attached', channel_state_change)

Push Notifications

Device Registration

// JavaScript - Register device for push notifications
ably.push.activate();

// JavaScript - Register device with FCM/APNs
ably.push.admin.deviceRegistrations.save({
  id: 'device-id',
  clientId: 'client-id',
  platform: 'ios',  // or 'android', 'browser'
  formFactor: 'phone',
  push: {
    recipient: {
      transportType: 'apns',
      deviceToken: 'device-token-from-apns'
    }
  }
});

Push Subscription

// JavaScript - Subscribe to push notifications
channel.push.subscribe((err) => {
  if (!err) {
    console.log('Subscribed to push notifications');
  }
});

// JavaScript - Unsubscribe from push notifications
channel.push.unsubscribe((err) => {
  if (!err) {
    console.log('Unsubscribed from push notifications');
  }
});

Publishing Push Notifications

// JavaScript - Publish with push notification
channel.publish({
  name: 'event-name',
  data: { message: 'Hello World' },
  extras: {
    push: {
      notification: {
        title: 'New Message',
        body: 'You have a new message',
        sound: 'default',
        icon: 'notification-icon'
      }
    }
  }
});
# Python - Publish with push notification
push_payload = {
    'notification': {
        'title': 'New Message',
        'body': 'You have a new message'
    }
}

channel.publish(
    name='event-name',
    data={'message': 'Hello World'},
    extras={'push': push_payload}
)

Integrations & Extensions

Webhooks

  1. Configure webhook in Ably dashboard
  2. Set endpoint URL where Ably will POST data
  3. Select events to trigger webhooks:
    • Channel lifecycle events
    • Channel occupancy events
    • Presence events
    • Message events
  4. Set signing key (optional) for verifying webhook authenticity

Reactor Functions

Integration Types:

  • AWS Lambda
  • Azure Functions
  • Google Cloud Functions
  • Custom HTTP endpoints
// Example AWS Lambda reactor function
exports.handler = async (event) => {
  const messages = event.messages || [];
  
  for (const message of messages) {
    console.log(`Received message: ${message.name}`, message.data);
    // Process message
  }
  
  return { statusCode: 200 };
};

Firehose

Stream Ably data to:

  • AWS Kinesis
  • AWS SQS
  • AWS Lambda
  • Google Cloud Pub/Sub
  • Apache Kafka
  • RabbitMQ
  • AMQP
  • MQTT

Configuration Steps:

  1. Define rule (channel/event patterns to match)
  2. Select destination service
  3. Configure authentication for destination
  4. Set message format and batching options

SSE (Server-Sent Events)

// Browser - Consume Ably channel as SSE
const sseUrl = `https://realtime.ably.io/event-stream?channels=example-channel&v=1.2&key=${encodeURIComponent('API-KEY')}`;
const eventSource = new EventSource(sseUrl);

eventSource.onmessage = (message) => {
  const data = JSON.parse(message.data);
  console.log('Received:', data);
};

eventSource.onerror = (error) => {
  console.error('SSE error:', error);
  eventSource.close();
};

Advanced Features

Channel Encryption

// JavaScript - Create and use encryption key
const cryptoKey = await Ably.Realtime.Crypto.generateRandomKey();
const channelOpts = { cipher: { key: cryptoKey } };
const channel = ably.channels.get('encrypted-channel', channelOpts);

// Publishing and subscribing works the same as normal channels
// but data is automatically encrypted/decrypted
# Python - Channel encryption
from ably.util.crypto import generate_random_key

crypto_key = generate_random_key()
channel_opts = {'cipher': {'key': crypto_key}}
channel = ably.channels.get('encrypted-channel', **channel_opts)

Connection Recovery

// JavaScript - Get connection recovery key
const recoveryKey = ably.connection.key;
console.log('Save this recovery key:', recoveryKey);

// Reconnect with recovery key later
const ably = new Ably.Realtime({ 
  key: 'your-api-key',
  recover: recoveryKey 
});

REST vs Realtime

FeatureRESTRealtime
ConnectionNon-persistent HTTPPersistent WebSocket
PublishingHTTP requestOver existing connection
SubscribingNot supported nativelyReal-time push
PresenceQuery onlyFull presence capabilities
Use Cases• Publishing only<br>• Server-side publishing<br>• Low-frequency updates• Real-time applications<br>• Need for subscriptions<br>• Presence functionality

Client Library Options

OptionDescriptionDefault
authUrlAuthentication URLNone
authCallbackAuthentication callback functionNone
keyAPI keyNone
tokenAuthentication tokenNone
clientIdClient identifierNone
environmentAbly environment‘production’
logLevelLogging verbosity2 (warn)
transportsPreferred transports[‘web-socket’, …]
fallbackHostsAlternative hosts for connectionDefault Ably hosts
recoverConnection recovery keyNone
idempotentRestPublishingEnable idempotent publishingfalse
autoConnectConnect automaticallytrue
useBinaryProtocolUse binary protocoltrue
queueMessagesQueue messages when disconnectedtrue
echoMessagesReceive messages published by selftrue

Performance & Scaling

Best Practices

  • Use Token Authentication for client-side applications
  • Implement Exponential Backoff for reconnection attempts
  • Batch Messages where appropriate
  • Filter Messages using channel namespaces and message filtering
  • Monitor Connection State to handle disconnections gracefully
  • Use Presence Deltas to reduce bandwidth for large presence sets
  • Consider SSE for browser clients that only need to subscribe
  • Use Binary Protocol for more efficient communication
  • Implement Heartbeats to detect connection issues early

Rate Limits & Quotas

Limit TypeDefaultEnterprise
Messages per second per connection100Customizable
Messages per second per channel160Customizable
Connections per accountBased on planCustomizable
Channels per accountBased on planCustomizable
Message size128KB128KB
Presence members per channel1,000Customizable

Handling Connection Issues

  1. Implement Connection State Listeners
  2. Queue messages during disconnections
  3. Use Exponential Backoff for reconnection attempts
  4. Implement Offline Storage for critical data
  5. Provide User Feedback about connection status
  6. Use Connection Recovery Key for seamless recovery

Debugging & Monitoring

Debugging Tools

  • Ably Debugger: Real-time message inspector in dashboard
  • History Explorer: View persisted message history
  • Connection Status: Check connection state in dashboard
  • Client Library Logs: Enable verbose logging
// JavaScript - Enable verbose logging
const ably = new Ably.Realtime({ 
  key: 'your-api-key',
  logLevel: 4  // verbose
});
# Python - Enable verbose logging
import logging
logging.basicConfig(level=logging.DEBUG)
ably = AblyRest('your-api-key', log_level=logging.DEBUG)

Monitoring & Metrics

Dashboard Metrics:

  • Connection counts
  • Message counts
  • API requests
  • Channel counts
  • Peak connection counts
  • Error rates

Custom Monitoring:

  • Use the Ably Control API
  • Set up Webhooks for system events
  • Implement custom health checks

Common Use Cases & Patterns

Live Chat

// JavaScript - Basic chat implementation
const chatChannel = ably.channels.get('chat-room');

// Send message
function sendMessage(text) {
  chatChannel.publish('message', { 
    text: text,
    sender: currentUser,
    timestamp: Date.now()
  });
}

// Receive messages
chatChannel.subscribe('message', (message) => {
  displayMessage(message.data);
});

// Show who's online
chatChannel.presence.subscribe((presenceMsg) => {
  updateUserList(presenceMsg);
});

// Join chat
chatChannel.presence.enter({ status: 'online' });

Real-time Dashboards

// JavaScript - Dashboard updates
const dashboardChannel = ably.channels.get('dashboard-updates');

// Subscribe to different metrics
dashboardChannel.subscribe('user-count', (message) => {
  updateUserCounter(message.data);
});

dashboardChannel.subscribe('system-load', (message) => {
  updateLoadChart(message.data);
});

dashboardChannel.subscribe('errors', (message) => {
  showErrorAlert(message.data);
});

Collaborative Editing

// JavaScript - Collaborative editing with OT
const docChannel = ably.channels.get(`document:${docId}`);

// Send operation
function sendOperation(operation) {
  docChannel.publish('operation', {
    operation: operation,
    userId: currentUser,
    version: currentVersion++
  });
}

// Receive operations
docChannel.subscribe('operation', (message) => {
  if (message.data.userId !== currentUser) {
    applyOperation(message.data.operation);
  }
});

// Show active users
docChannel.presence.subscribe((presenceMsg) => {
  updateCollaboratorList(presenceMsg);
});

Geolocation Tracking

// JavaScript - Location tracking
const locationChannel = ably.channels.get('location-updates');

// Send location
function sendLocation(position) {
  locationChannel.publish('position', {
    latitude: position.coords.latitude,
    longitude: position.coords.longitude,
    userId: currentUser,
    timestamp: Date.now()
  });
}

// Track user on map
navigator.geolocation.watchPosition(sendLocation);

// Receive other users' locations
locationChannel.subscribe('position', (message) => {
  if (message.data.userId !== currentUser) {
    updateMarkerPosition(message.data);
  }
});

Resources for Further Learning

Official Documentation

Community & Support

Sample Applications


Note: This cheat sheet provides a comprehensive overview of Ably’s features and capabilities as of April 2025. Always refer to the official documentation for the most up-to-date information and best practices.

Scroll to Top