Messages
Send, update, delete, and receive messages in a chat room with any number of participants. Users subscribe to messages by registering a listener, and send messages to all users that are subscribed to receive them.
A user can also update or delete a message, all users that are subscribed to the room will be notified of the changes.
Subscribe to messages
Subscribe to receive messages in a room by registering a listener. Use the messages.subscribe()
method in a room to receive all messages that are sent to it:
const {unsubscribe} = room.messages.subscribe((event) => {
console.log(event.message);
});
CopyCopied!
Message structure
The following is the structure of a message:
{
"serial": "01826232498871-001@abcdefghij:001",
"clientId": "basketLover014",
"roomId": "basketball-stream",
"text": "What a shot!",
"headers": {},
"metadata": {},
"createdAt": new Date("2024-06-12T11:37:59.988Z"),
"action": "message.create",
"version": "01826232498871-001@abcdefghij:001",
"timestamp": new Date("2024-06-12T11:37:59.988Z"),
"operation": {},
}
CopyCopied!
The following are the properties of a message:
Property | Description | Type |
---|---|---|
serial | An Ably-generated ID used to uniquely identify the message. By comparing it to others it provides a deterministic global ordering of messages. | String |
clientId | The client identifier of the user that created the message. | String |
roomId | The name of the room the message was created in. | String |
text | The message contents. | String |
headers | Optional headers for adding additional information to a message, such as the relative timestamp of a livestream video, or flagging a message as important. Do not use the headers for authoritative information. There is no server-side validation. When reading headers treat them like user input. | Object |
metadata | Optional additional metadata about the message, such as animations, effects or links to other resources such as images. This information is not read by Ably. Do not use metadata for authoritative information. There is no server-side validation. When reading metadata treat it like user input. | Object |
createdAt | The time the message was created. | Date |
action | The latest action performed on this message, such as message.create , message.update or message.delete . | String |
version | An Ably-generated ID used to uniquely identify the version of the message. It provides a deterministic global ordering of message versions. The version is identical to serial if the action is message.create . | String |
timestamp | The time the action was performed. It will be identical to createdAt if the action is a message.create . | Date |
operation | For updates and deletions, this provides additional details about the action. It may contain the following properties: | Object or undefined |
clientId: The client identifier of the user associated with the action. | String or undefined | |
description: Optional description for the action. | String or undefined | |
metadata: Optional additional metadata about the action. | Object or undefined |
See below for more information on how to apply deterministic global ordering to the chat messages in your application.
Unsubscribe from messages
Use the unsubscribe()
function returned in the subscribe()
response to remove a chat message listener:
// Initial subscription
const { unsubscribe } = room.messages.subscribe((event) => console.log(event.message));
// To remove the listener
unsubscribe();
CopyCopied!
Use the messages.unsubscribeAll()
method to deregister all chat message listeners in a room:
await room.messages.unsubscribeAll();
CopyCopied!
Send a message
Use the messages.send()
method to send a message in a chat room. All users that are subscribed to messages on that room will receive it:
await room.messages.send({text: 'hello'});
CopyCopied!
Update a message
Use the messages.update()
method to update a message in a chat room. All users that are subscribed to messages on that room will receive the update:
import { Message } from '@ably/chat';
const messageToUpdate: Message
await room.messages.update(messageToUpdate, { text: "my updated text" }, { description: "Message update by user" });
CopyCopied!
Filter for updates
Use the messages.subscribe()
method to receive messages in a room. To filter for updated messages, provide a listener that checks the type
property of the message event:
import { MessageEvents } from '@ably/chat';
const {unsubscribe} = room.messages.subscribe((event) => {
switch (event.type) {
case MessageEvents.Created:
console.log('Received message: ', event.message);
break;
case MessageEvents.Updated:
const existing = myMessageList.find(event.message);
if (existing && event.message.versionBefore(existing)) {
// We've already received a more recent update, so this one can be discarded.
return;
}
console.log('Message updated: ', event.message);
break;
default:
break;
}
});
CopyCopied!
See below for more information on how to deterministically apply ordering to update events in your application.
Message update structure
The following is the structure of an updated message:
{
"serial": "01726232498871-001@abcdefghij:001",
"clientId": "basketLover014",
"roomId": "basketball-stream",
"text": "What a shot! Edit: I meant to say 'What a dunk!'",
"headers": {},
"metadata": {},
"createdAt": new Date("2024-06-12T11:37:59.988Z")S,
"action": "message.update",
"version": "01826232498871-001@abcdefghij:001",
"timestamp": new Date("2024-11-21T15:49:25.425Z"),
"operation": {
"clientId": "basketLover014",
"description": "Message updated by client",
"metadata": {}
},
}
CopyCopied!
The updated message response is identical to the structure of a message, with the following differences:
Property | Description |
---|---|
action | Set to message.update . |
version | Set to the serial of the update action. |
timestamp | Set to the time the message was updated. |
operation | Set to the details the actioning client provided in the request. |
Delete a message
Use the messages.delete()
method to delete a message in a chat room. All users that are subscribed to messages on that room will receive the deletion:
import { Message } from '@ably/chat';
const messageToDelete: Message
await room.messages.delete(messageToDelete, { description: 'Message deleted by user' });
CopyCopied!
Filter for deletes
Use the messages.subscribe()
method to receive messages in a room. To filter for deleted messages, provide a listener that checks the type
property of the message event:
import { MessageEvents } from '@ably/chat';
const {unsubscribe} = room.messages.subscribe((event) => {
switch (event.type) {
case MessageEvents.Created:
console.log('Received message: ', event.message);
break;
case MessageEvents.Deleted:
const existing = myMessageList.find(event.message);
if (existing && event.message.versionBefore(existing)) {
// We've already received a more recent update, so this one can be discarded.
return;
}
console.log('Message deleted: ', event.message);
break;
default:
break;
}
});
CopyCopied!
See below for more information on how to deterministically apply ordering to delete events in your application.
Message deletion structure
The following is the structure of a deleted message:
{
"serial": "01726232498871-001@abcdefghij:001",
"clientId": "basketLover014",
"roomId": "basketball-stream",
"text": "What a shot!",
"headers": {},
"metadata": {},
"createdAt": new Date("2024-06-12T11:37:59.988Z"),
"action": "message.delete",
"version": "01826232498871-001@abcdefghij:001",
"timestamp": new Date("2024-11-21T15:49:25.425Z"),
"operation": {
"clientId": "basketLover014",
"description": "Message deleted by client",
"metadata": {}
},
}
CopyCopied!
The deleted message response is identical to the structure of a message, with the following differences:
Property | Description |
---|---|
action | Set to message.delete . |
version | Set to the serial of the deletion action. |
timestamp | Set to the time the message was deleted. |
operation | Set to the details the actioning client provided in the request. |
Ordering chat message events
Chat messages and update events are delivered in realtime to clients connected to a particular region in the order in which that region receives them. The order in which a given region receives these events may be different from the “global” order of events, i.e. the true time-based order in which events happened.
Chat messages are uniquely identified by their serial
and may have multiple versions
as a result of edit and delete operations. Both serial
and version
are lexicographically sortable strings. This means they can be used to enforce a deterministic global ordering based on string comparison.
Ordering New Messages
If the serial
of one message occurs before another when lexicographically sorted, the first message is considered to have occurred before the other. If the serial
values are identical, the messages are the same message.
The Message
object also has convenience methods before
, after
and equal
which provide the same comparison.
Ordering Updates and Deletes
Applying an action to a message produces a new version, which is uniquely identified by the version
property. When two message instances share the same serial
they represent the same chat message, but they can represent different versions. Lexicographically sorting the two message instances by the version
property gives the global order of the message versions: the message instance with a greater version
is newer, the message instance with a lower version
is older, and if their version
is equal then they are the same version.
The Message
object also has convenience methods versionBefore
, versionAfter
and versionEqual
which provide the same comparison.
Update and Delete events provide the full message payload, so may be used to replace the entire earlier version of the message.