LiveObjects operations define how object data is updated and synchronized across multiple clients.
When you create or update an object, the change is expressed as an operation that is sent as an object message on the channel. The operation is then applied to the object instance on all clients that are subscribed to the channel.
This document explains the key concepts you need to know when working with operations.
Operation types
Each object type supports specific operations that modify the object's data.
LiveMap operations
LiveMap supports the following operations:
set: Set a value for a keyremove: Remove a key and its value
The value of an entry in a LiveMap instance can be a primitive type or a reference to another object.
1
2
await myObject.get('user').set('username', 'alice');
await myObject.get('user').remove('status');LiveCounter operations
LiveCounter supports the following operations:
increment: Increment the counter by a specified amountdecrement: Decrement the counter by a specified amount
The amount is a double-precision floating-point number, which is the same as underlying type of a LiveCounter value.
1
2
await myObject.get('visits').increment(5);
await myObject.get('score').decrement(2);Create operations
Create operations are used to instantiate new objects of a given type.
Use LiveMap.create() and LiveCounter.create() to create new objects. These methods create special value types that can be assigned directly to paths:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Create and assign a map with initial values
await myObject.set('user', LiveMap.create({
username: 'alice',
status: 'online'
}));
// Create and assign a counter with initial value
await myObject.set('score', LiveCounter.create(100));
// Create nested structures
await myObject.set('profile', LiveMap.create({
name: 'Alice',
score: LiveCounter.create(0),
settings: LiveMap.create({
theme: 'dark',
notifications: true
})
}));When a create operation is processed, an object ID is automatically generated for the new object instance.
Object IDs
Every operation is expressed relative to a specific object instance, identified by its object ID, which determines which object the operation is applied to.
When using a PathObject, the specific object instance at the given path is evaluated at the time a method is called that updates the object. The client library will then publish an operation targeting the resolved object.
When using a client library object IDs are handled automatically, allowing you work directly with object references:
1
2
3
4
5
6
7
8
9
// The published operation targets the object ID of the object at 'user'
await myObject.get('user').set('username', 'alice');
// Get the specific instance and its ID
const userInstance = myObject.get('user').instance();
if (userInstance) {
console.log('Object ID:', userInstance.id());
await userInstance.set('username', 'alice');
}In the REST API, the relationship between operations and object IDs is made explicit:
curl -X POST https://main.realtime.ably.net/channels/my-channel/objects \
-u "demokey:*****"
-H "Content-Type: application/json" \
--data \
'{
"operation": "MAP_SET",
"objectId": "root",
"data": {"key": "username", "value": {"string": "alice"}}
}'Batch operations
Batch operations can be used to batch a set of operations together:
- Multiple operations are grouped into a single atomic unit
- All operations in the batch either succeed together or fail together
- Operations in a batch are sent as a single message
- No operations from other clients can be interleaved within a batch
Object message properties
Each operation is carried by an ObjectMessage, which is surfaced in subscriptions and provides metadata about the operation and who performed it.
ObjectMessage
The following are the properties of an ObjectMessage:
| Property | Description |
|---|---|
| id | Unique ID assigned by Ably to this object message |
| clientId | The ID of the client that published this operation |
| connectionId | The ID of the connection used to publish this operation |
| timestamp | The timestamp of when the object message was received by Ably, as milliseconds since the Unix epoch |
| channel | The name of the channel the object message was published to |
| operation | An ObjectOperation describing the operation that was applied |
| serial | An opaque string that uniquely identifies this object message |
| serialTimestamp | A timestamp derived from the serial field |
| siteCode | An opaque string that uniquely identifies the Ably site the object message was published to |
| extras | A JSON object of arbitrary key-value pairs that may contain metadata, and/or ancillary payloads. Valid payloads include headers |
ObjectOperation
The operation field of an ObjectMessage contains an ObjectOperation with the following properties:
| Property | Description |
|---|---|
| action | The operation action. One of: 'map.create', 'map.set', 'map.remove', 'counter.create', 'counter.inc', or 'object.delete' |
| objectId | The ID of the object the operation was applied to |
| mapOp | Present for map mutation operations ('map.set', 'map.remove'). Contains key (the key that was modified) and optionally data (an ObjectData representing the value assigned to the key, present only for 'map.set' operations) |
| counterOp | Present for counter increment operations ('counter.inc'). Contains amount (the value added to the counter) |
| map | Present for 'map.create' operations. Defines the initial value of the map object with optional semantics (conflict-resolution strategy) and entries (initial key-value pairs) |
| counter | Present for 'counter.create' operations. Defines the initial value of the counter object with optional count (initial counter value) |
ObjectData
The data field in mapOp is an ObjectData object that represents the value assigned to a map key. It has the following properties:
| Property | Description |
|---|---|
| objectId | A reference to another object (such as a LiveMap or LiveCounter) by its object ID. Present when the value is a LiveObject |
| value | A decoded primitive value (string, number, boolean, JSON-serializable object or array, or binary data). Present when the value is a primitive type |