Operations

Open in

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 key
  • remove: 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 amount
  • decrement: 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:

JavaScript

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:

PropertyDescription
idUnique ID assigned by Ably to this object message
clientIdThe ID of the client that published this operation
connectionIdThe ID of the connection used to publish this operation
timestampThe timestamp of when the object message was received by Ably, as milliseconds since the Unix epoch
channelThe name of the channel the object message was published to
operationAn ObjectOperation describing the operation that was applied
serialAn opaque string that uniquely identifies this object message
serialTimestampA timestamp derived from the serial field
siteCodeAn opaque string that uniquely identifies the Ably site the object message was published to
extrasA 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:

PropertyDescription
actionThe operation action. One of: 'map.create', 'map.set', 'map.remove', 'counter.create', 'counter.inc', or 'object.delete'
objectIdThe ID of the object the operation was applied to
mapOpPresent 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)
counterOpPresent for counter increment operations ('counter.inc'). Contains amount (the value added to the counter)
mapPresent for 'map.create' operations. Defines the initial value of the map object with optional semantics (conflict-resolution strategy) and entries (initial key-value pairs)
counterPresent 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:

PropertyDescription
objectIdA reference to another object (such as a LiveMap or LiveCounter) by its object ID. Present when the value is a LiveObject
valueA decoded primitive value (string, number, boolean, JSON-serializable object or array, or binary data). Present when the value is a primitive type