Best Practice Guide

This best practice guide is designed to help you make the most out of Ably’s Realtime platform. These recommendations include some important considerations that may otherwise be missed when reading through our documentation.

This guide consists of the following two sections:

  • Ably 101 – Key concepts that you must understand before moving on to the best practice recommendations.
  • For experienced developers – Ably’s recommendations for experienced developers, to help avoid common issues.

Ably 101

Ably Realtime is a cloud-based platform that enables you to integrate realtime functionality into your applications.

Authentication

Ably supports two methods of authentication:

1. Basic authentication
2. Token authentication

When using basic authentication, you use the Ably API key to authenticate with Ably. In production systems you should always avoid using basic authentication on the client-side (such as in a browser), where the API key might be exposed. Instead, use token authentication with either an authUrl or authCallback.

With token authentication, your auth server uses your Ably API key to authenticate with Ably and then provides a TokenRequest object to your client, which your client then uses to authenticate with Ably. These short-lived tokens ensure that the Ably API key is never exposed to your client-side applications. However, the auth server can use the API key directly to authenticate with Ably using basic authentication, as on the secure server the API key will not be accessible to anyone other than authenticated personnel, such as systems administrators.

Important: Do not use basic authentication on the client-side, as it exposes your Ably API key. API keys don’t have an expiry, so once compromised, they could be used indefinitely by an attacker. Tokens have an expiry, and so there is only a small period of time during which the compromised token can be used.

Important: Never use token authentication to authenticate your server with Ably, as this results in unnecessary overhead – the server will periodically need to reauthenticate with Ably. Instead, use basic authentication to authenticate your server with Ably.

If you prefer to use a single auth strategy across all third-party services, you can use JWTs to authenticate with Ably.

Using the API key on the client side

Basic authentication is the simplest method to authenticate your clients on the Ably platform, but this exposes your API key in the source code. Therefore, Ably recommends that you create an auth server and use token authentication to authenticate your clients. Using this approach, only your auth server has access to your API key, protecting it from being misused. Read more about selecting an authentication mechanism.

Renewing tokens

Tokens are short-lived, that is, they expire after a given time. You must ensure that the client can request a new token when it needs one. The Ably Client Library SDKs provide two different mechanisms to obtain tokens automatically: authUrl or authCallback. You specify which one to use in the ClientOptions object.

Realtime vs REST

Ably Client Library SDKs may implement two interfaces: realtime and REST. The Realtime interface maintains a persistent connection to Ably and can be considered stateful. The REST interface issues HTTP requests to the Ably platform and is stateless. The REST interface is therefore lighter weight because it does not need to maintain state.

Depending on your use case, you may only need to use the REST interface on your server. For example, the server may only need to authenticate with Ably, and then act as an auth server for clients. You might then use the realtime interface on your client, so it can both subscribe to and publish messages.

The PHP and Python SDKs only have a REST interface. While the Ruby SDK implements both realtime and REST interfaces, there is also a Ruby SDK available that implements only the REST interface. The full list of available SDKs, and their supported interfaces, can be found on the SDK download page.

An important difference between the REST and realtime interface is that with REST you can only publish realtime messages and not subscribe to them. With the realtime interface you can both publish and subscribe. If you have a use case where you only need to authenticate, or publish messages, then the REST interface is recommended.

The main factors are summarized here:

  • If you want to maintain a persistent connection with Ably’s Realtime platform in order to subscribe to messages sent on a channel, or be notified whenever the presence set changes, you need to use the realtime library.
  • If you just want to publish data to the platform and don’t need to subscribe or manage channel attachment, you can the REST library.
  • If you want to publish at very high rates or with the lowest possible realtime latencies, Ably’s realtime library would be best as it can publish at higher rates and lower latencies than is possible with the REST library due to message pipelining.
  • If you want to primarily issue tokens for clients or publish messages to channels, you can use the REST library.
  • If you want to get one-time info about the presence set of a channel, you can use the REST library.
  • If you want to set up realtime communication with IoT devices on platforms for which there isn’t an Ably Client Library SDK available, you can use the MQTT protocol adapter.
  • If you wish to migrate from PubNub or Pusher to Ably, you can use the relevant protocol adapter to switch to Ably, and then gradually move over to using the Ably Client Library SDKs.

Encryption

All Ably Client Library SDKs use TLS by default. This ensures that data is transmitted to and from Ably securely. However, messages are not encrypted within the Ably system. Using the encryption feature of our SDKs, you can ensure that message payloads are opaque: they can never be decrypted by Ably but only by other clients that share your secret key.

Publish and Subscribe (Pub/Sub)

You can publish data to named channels within Ably’s platform and the platform will make sure that all clients subscribed to those channels will receive the data in realtime.

Notifications

Ably offers the Push Notifications feature which enables you to send notifications to your clients even when they are not currently using your application and are therefore not connected to Ably. This can be implemented on both mobile devices and browsers.

Presence

With Ably’s Presence feature, your clients can attach to a presence channel and announce to other channel users that they are present on a channel. This is very useful, for example, in chat apps to indicate a member is online, or with IoT devices to indicate a device is online and connected. Clients can subscribe to realtime presence changes on channels, such as members entering or leaving, and respond to these events accordingly.

For experienced developers

In order to make the best use of Ably, you need to be aware of the following information:

Attaching versus subscribing to a channel

It is important to understand the difference between attaching and subscribing to a channel, and that messages are sent to clients as soon as they attach to a channel.

The attach method attaches a client to the channel. Published messages are immediately sent to clients if they have subscribe capabilities to that channel, regardless of whether or not the client has subscribed to the channel.

The subscribe method registers a subscribe listener for messages received on the channel. subscribe is a client-side operation, meaning Ably is unaware of whether or not a client is subscribed to the channel. attach is called implicitly by the subscribe method.

Detaching versus unsubscribing from a channel

It is important to understand the difference between detaching and unsubscribing from a channel, and that messages will continue to be sent to clients if they only call the unsubscribe() method.

The detach() method detaches a client from the channel. The client will no longer receive any messages published to the channel.

The unsubscribe() method removes message listeners for the channel. unsubscribe() is a client-side operation, meaning Ably is unaware of whether or not a client has unsubscribed from the channel. Messages will continue to be streamed to the client until detach() is called.

Calling subscribe() for a channel implicitly attaches the client to the channel as well. It is important to note that if you call subscribe() followed by unsubscribe(), the client remains attached to the channel.

Use separate channels to filter messages for different sets of subscribers

By default, all data published to a channel is pushed by Ably to every subscriber attached to that channel. Only on the client side can subscribers filter messages by their message name and so ‘avoid’ receiving all published messages. But, if you have messages that don’t need to be sent to some clients at all, you’re often better off using a separate channel for them rather than implementing a filter. Because this way, you won’t be sending unnecessary messages that count towards your monthly message quota. This may be economical, but it isn’t always the best solution as in some cases you may wish to trigger different listeners for different events and so setting up a single channel and filtering the messages client side would be more sensible. An example is shown below:

const sportsChannel = realtime.channels.get("sport");
//publish
sportsChannel.publish("update", { "team": "Man United" });
sportsChannel.publish("add", { "team": "Chelsea" });

//subscribe filters
sportsChannel.subscribe("update", () => {
  //do one thing
});
sportsChannel.subscribe("add", () => {
  //do another thing
});

Handling connection and channel state-change events

Ably’s Realtime SDKs automatically handle disruptions such as network disconnections, so you don’t need to manually handle reconnection. The library emits:

  • Connection state change events such as initialized, connecting, etc.You can set up listeners for these state change events to handle connections failures like those cause by failed authentication. An example of handling connection state-change event is shown below:
realtime.connection.on((stateChange) => {
  console.log('New connection state is ' + stateChange.current);
});
  • Channel state change events for various channel states such as initialized, attaching, etc. Similar to connection state changes, you can set up listeners for channel state changes in case you’d like to be informed about what’s happening. For example, if you want to know whenever the library has lost message continuity on a channel due to being disconnected for more than two minutes. An example of handling channel state-change events is shown below:
const myListener = (stateChange) => {
  console.log('channel state is ' + stateChange.current);
  console.log('previous state was ' + stateChange.previous);
});
channel.on(myListener);

However, while registering listeners for both connection and channel state change events, bear in mind that certain repeating states might add new listeners each time. For instance, registering a listener for on(connected) adds a new listener every time the client becomes connected, even if this is a reconnection after being offline for a while, which is often an unintentional result.

Unsubscribe and close versus off

It is important to understand the difference between unsubscribing from a channel and closing a connection, compared to calling the off() method for a channel or connection.

The unsubscribe() method removes message listeners for a channel.

The close() method closes a realtime connection.

The off() method for a channel or connection removes ChannelEvent or ConnectionEvent listeners that are listening for state changes on a channel or for a connection.

Subscribing to events on the server-side

You should be aware that subscribing to events on the server side, using the standard Pub/Sub pattern can be disadvantageous because it can increase latency or duplicate events among multiple servers, as explained in this blog article. With Message Queues, you can have multiple worker servers and allow Ably to distribute the load of the messages received from publishers, so that each message is only processed once by any one of your worker servers.

Choosing between events, message queues, and Firehose

Events work well for low or medium message rates of approximately 20/s, but Message Queues scale better to high message rates of around 200/s. For even higher rates, Ably recommends using Firehose.

Multiplexing channels

Always strive to instantiate a single Ably Client Library SDK instance per device, web page or server. Don’t forget that the SDK can multiplex many channels over a single connection. This maximizes throughput, minimizes bandwidth consumption, and reduces power usage.

Limiting the effect of a security compromise using the principle of least privilege

Using the available authentication methods in Ably, you can choose the permissions or capabilities to be given to a client. You can do this by restricting the permissions on the API key as well as restricting the permissions that your auth server grants to this API key whilst sending a token request. You can restrict the channels that a client can access (using wildcards), as well as the operations the client can perform within those channels, using capabilities.

Always make sure to limit the capabilities given to a client to only what’s required by the client to do their job. This prevents any unexpected behaviour by a client and increases the security of your application. This is called the principle of least privilege.

Efficient use of channels

Although Ably recommends that you use channels to distribute work more evenly across the cluster, there is an associated cost for a high number of channels. Here are some considerations that might help you plan your channel usage:

  • Don’t use different channels just to indicate different types of data or different events, if they’re all going to the same set of clients. Use one channel, and distinguish between the different events using the message name.
  • Channels are the unit of security and scalability. If you are sending some data that must not be shared with certain clients, make sure it’s on a channel that those clients don’t have the capabilities to attach to.
  • Be aware that each channel can support a maximum of 100 messages per second.

Embedding client ID in the token to ensure trust

A client ID serves as an identification for your clients when they are present on a channel. Your clients can set their own ID using the clientId attribute, but in that case, there’s a possibility that some of your clients can pretend to be someone else. To prevent this, you can embed a clientId in the token issued to your clients from your auth server. Doing so ensures that the clientId is set by your auth server, thus eliminating the chance of the end clients emulating as others.

Understanding the number of presence messages that can be produced

It is important when using the presence feature to understand the number of messages it can produce. If you have subscribers and presence members present on a channel then the number of messages used can increase rapidly. As an example, consider 200 clients subscribed to presence events on a channel and all of them join and leave the presence set within a few minutes. This would result in the following messages:

  • 200 presence messages published for the enter event.
  • 200 × 200 (40,000) messages subscribed to for the enter events.
  • 200 presence messages published for the leave event.
  • 200 × 200 (40,000) presence messages subscribed to for the leave event.

This highlights the potential for 80,400 messages to be sent in a very short space of time on a single channel. As additional clients subscribe and enter the presence set, the number of messages quickly multiplies.

In some instances this is the behavior required of your application. In other scenarios you may prefer to only have a server subscribed to presence events and the clients are only present on the channel.

Being present without subscribing to presence events

It is possible to have clients be present on a channel without them receiving presence change events. Efficient use of capabilities can prevent all clients from receiving presence notifications while still allowing them to enter the presence set. This is a common scenario when only a server is required to monitor the presence set and saves clients from subscribing to a potentially high volume of unnecessary messages.

An example of achieving this would be to use one channel for generic communications and another for the presence set. The following capabilities demonstrate this for clients and for servers:

For clients:

{
  "chatPresence": ["presence"],
  "chat": ["publish", "history", "subscribe"],
}

For servers:

{
  "chatPresence": ["presence", "subscribe"],
  "chat": ["publish", "history", "subscribe"],
}

Alternatively, you can use channel mode flags to specify a subset of capabilities available on the API key or token a client is using.

In the following example the client would be present on the channel without receiving presence notifications from other clients in the presence set:

. const realtime = new Ably.Realtime('xVLyHw.46CtVQ:K9jUjDwpjcPQ0zpLJ0haf-k7rVGb5CXWYIe0-Ex-Brc');
const channelOptions = {
  modes: ['PUBLISH', 'SUBSCRIBE', 'PRESENCE']
};
const channel = realtime.channels.get('ale-egg-red'), channelOptions);

As this is server-side filtering, clients won’t be receiving presence notifications which saves a potentially high volume of messages from being used.

Using the synced presence set provided by the realtime library

Monitoring presence events can be quite complex. For this reason, the Presence object in the realtime library exposes a get method allowing a client to retrieve an array of all members present on the channel. The Ably client is responsible for keeping track of the presence set from the time that the channel is attached. An up-to-date presence set is pushed to the client following attach and this set is updated on each subsequent presence event. Thus get returns the already-known presence set retained in memory and does not trigger a new request to the Ably service. You can simply use this method as shown below, instead of venturing into the complexity of building one of your own.

channel.presence.get((err, members) => {
  console.log('There are ' + members.length + ' members on this channel');
  console.log('The first member has client ID: ' + members[0].clientId);
});

Using the presence set as a single source of truth to see which clients are present

A single clientId may be present multiple times on the same channel via different client connections. As far as Ably is concerned, these are different members of the presence set for the channel, however they will be differentiated by their unique connectionId. For example, if a client with ID “Sarah” is connected to a chat channel on both a desktop and a mobile device simultaneously, or maybe via multiple tabs in a browser, “Sarah” will be present twice in the presence member set with the same clientID, yet will have two unique connection IDs. This means that if “Sarah” leaves the channel on her mobile, it doesn’t necessarily mean she’s gone as she might still be connected via desktop. For this reason, you should always use the presence set, which you can retrieve using the get method, as the single source of truth to see which clients are connected.

App testing

Ably apps are entirely isolated from one another, so each app can be treated as its own environment. Ably recommends using separate apps within your account for testing purposes and to name apps logically to easily differentiate between development, test and production environments.

The Ably sandbox environment is strictly for use by Client Library SDK developers and should not be used for testing.


Need help?

If you need any help with your implementation or if you have encountered any problems, do get in touch. You can also quickly find answers from our knowledge base, and blog.