Skip to content

[AIT-31] LiveObjects Path-based API product docs for JavaScript SDK#3019

Merged
mschristensen merged 1 commit intomainfrom
AIT-31/objects-path-api
Dec 19, 2025
Merged

[AIT-31] LiveObjects Path-based API product docs for JavaScript SDK#3019
mschristensen merged 1 commit intomainfrom
AIT-31/objects-path-api

Conversation

@VeskeR
Copy link
Contributor

@VeskeR VeskeR commented Dec 11, 2025

Description

Product docs for LiveObjects Path-based API for JavaScript SDK.
See:

Resolves AIT-31

Checklist

@coderabbitai
Copy link

coderabbitai bot commented Dec 11, 2025

Important

Review skipped

Auto reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch AIT-31/objects-path-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@VeskeR VeskeR changed the title [WIP] LiveObjects Path-based API product docs for JavaScript SDK [WIP] [AIT-31] LiveObjects Path-based API product docs for JavaScript SDK Dec 11, 2025
@VeskeR VeskeR force-pushed the AIT-31/objects-path-api branch from 41ef613 to 19a241e Compare December 12, 2025 17:27
@VeskeR VeskeR marked this pull request as ready for review December 12, 2025 17:28
@VeskeR VeskeR changed the title [WIP] [AIT-31] LiveObjects Path-based API product docs for JavaScript SDK [AIT-31] LiveObjects Path-based API product docs for JavaScript SDK Dec 12, 2025
@VeskeR
Copy link
Contributor Author

VeskeR commented Dec 12, 2025

One concern I have regarding describing one of the benefits of Instance subscriptions as tracking objects "as they move" through the object tree, e.g. here:

Use Instance when you need to:

  • Track a specific object instance as it moves through the structure

"Issue": There's actually currently no way to "move" an object via the Realtime API - no OBJECT_MOVE operation exists, and you can't set a map key to an existing object instance, only create new objects with LiveMap.create()/LiveCounter.create(). Moving is only achievable via REST API and by using object IDs explicitly.

Options:

  1. Remove "movement" language and focus on other Instance benefits (getting object ID, deletion notifications)
  2. Explicitly mention that object "movement" is only possible via REST API requests (we don't have a REST SDK client ready though, so won't be able to provide code samples unfortunately how it would look like)

Thoughts? @lawrence-forooghian @mschristensen

@lawrence-forooghian lawrence-forooghian added the review-app Create a Heroku review app label Dec 15, 2025
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 15, 2025 12:45 Inactive
Copy link
Contributor

@lawrence-forooghian lawrence-forooghian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a big PR, nice work 😅 I've had a look through all of it and left some comments, nothing major though

Ably LiveObjects provides the following key features:

* [Object types](#object-types)
<If lang="javascript">* [Path-based API](#path-based-api)</If>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem to be rendering correctly:

Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might it be worth explicitly mentioning, in the places where we talk about the path-based API, that it's only available in JS at the moment and that Swift and Kotlin still use an older API?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is no language selector in this page (presumably because there are no code snippets?) so it's unclear to me that these conditional blocks are really doing anything

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If-ed the whole bullet list to make sure it renders correctly and added languages list for this page in nav configs - not sure what's a condition for it to appear automatically, but now it works as expected.

Might it be worth explicitly mentioning, in the places where we talk about the path-based API, that it's only available in JS at the moment and that Swift and Kotlin still use an older API?

I believe most places will have Ifs to handle that, but I added a not for new PathObject and Instance pages to indicate they're JS specific

</Code>

The subscription callback receives an event object with:
- `object`: A `PathObject` representing the updated path
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure that "updated path" makes sense (the path hasn't changed; its contents have)

Suggested change
- `object`: A `PathObject` representing the updated path
- `object`: A `PathObject` representing the path at which there was an object change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


// Access via path
const settings = myObject.get('settings');
await settings.set('fontSize', 14);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgive my ignorance here but what happens if the user tries setting fontSize before the client receives the map created in myObject.set('settings')? Does the client end up potentially updating the map (if any) that was previously at the settings entry instead, thus causing the fontSize change to be lost?

Copy link
Contributor Author

@VeskeR VeskeR Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes ,this can happen as we still don't have apply operation on ACK implemented. This behavior is documented for all publish operation and subscription methods.
That said, I think this code example is simple enough to just drop an "access" part (as its main focus is to show how to create an object, just like java/swift examples below) bit so we can avoid the need to clarify this behavior here.

// Increment the counter
await visitsCounter.increment(1);
// Access via path
const visits = myObject.get('visits');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a similar question here to above; in this scenario, is it possible that the counter that gets incremented here is not the one that they created in the myObject.set('visits', LiveCounter.create(0)) call?

* Serves as the [entry point](#reachability) for accessing all other objects on a channel

Access the root object using the `getRoot()` function:
Access the root object using `channel.object.get()`:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that this needs to be language-<If>-d?


### entries() method <a id="entries"/>

The `entries()` method returns an iterator of `[key, PathObject]` pairs:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious why we chose to return an iterator and not just an array? Is it a common thing to do in JS? I'd get it if perhaps we wanted to do some sort of lazy streaming of results or something, but otherwise it seems it's something that's harder to log to the console and which some developers might not be familiar with.

link: '/docs/liveobjects/concepts/path-object',
},
{
name: 'Object Instance',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think just Instance would be OK? else lowercase the "Instance"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it should be Instance


<Code>
```javascript
myObject.get('visits').subscribe(({ object, message }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something I've been wondering whilst looking at this API — are the subscriptions added at a path level or at a PathObject instance level? That, is, if I write

const pathObject = myObject.get('visits').subscribe(() => {
  console.log("pathObject listener fired")
});

// pathObject presumably gets garbage collected shortly after since the user has no reference to it?

— does the listener still get called when events happen at that path even though the PathObject instance through which they subscribed no longer exists?


// Remove a key
await map.remove('username');
// Using PathObject
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that if you wish to show both the PathObject and Instance APIs here (which I'm not sure you need to do since it's not that important for explaining the semantics of the operations) then it might be better as two separate code blocks to make it clear they're not related


// Set a nested map
await myObject.get('settings').set('advanced', LiveMap.create({
debugMode: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor: maybe we could pick some other example values? this confused me at first because it looked like they were keys that control the behaviour of LiveMap.create

@lawrence-forooghian
Copy link
Contributor

lawrence-forooghian commented Dec 15, 2025

Explicitly mention that object "movement" is only possible via REST API requests (we don't have a REST SDK client ready though, so won't be able to provide code samples unfortunately how it would look like)

I think that whatever it is we do, we should handle the mention of cyclic references in the same way (either not mention them or mention them and explain the limited circumstances in which they might occur); I'd lean towards the latter.

Copy link
Contributor

@mschristensen mschristensen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitting my review with initial comments - we caught up on a call and I will take this further forward based on our discussions :)

Ably LiveObjects provides the following key features:

* [Object types](#object-types)
<If lang="javascript">* [Path-based API](#path-based-api)</If>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, there is no language selector in this page (presumably because there are no code snippets?) so it's unclear to me that these conditional blocks are really doing anything

reactionsMap.subscribe(() => {
// Subscribe to map updates
reactionsMap.subscribe(({ object }) => {
console.log('Reactions map updated:', [...reactionsMap.entries()]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to suggest:

  console.log('Reactions map updated:', [...object.entries()]);

but then I realised that's wrong, as object might be some nested object that changed.

I think we need to add some context here explaining this, at least at the surface level, as on initial read the user immediately wonders why the counter example uses the object but the map example doesn't

## Update objects <a id="step-7"/>

Update objects using mutation methods. All subscribers (including you) will be notified of the changes when you update an object:
Update objects using mutation methods on `PathObject`. All subscribers (including you) will be notified of the changes:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Update objects using mutation methods on `PathObject`. All subscribers (including you) will be notified of the changes:
Update objects using mutation methods on `PathObject`. All subscribers, including the client that made the update, will be notified of the changes:

console.log('Visits counter updated:', visitsCounter.value());
// Subscribe to counter updates
visitsCounter.subscribe(({ object }) => {
console.log('Visits updated:', object.value());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To align this log with the examples that follow:

Suggested change
console.log('Visits updated:', object.value());
console.log('Visits counter updated:', object.value());

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should update this - we call it the "channel object" in the index page, we should use that framing here too

| `path()` | Get the string representation of the path | All paths |
| `value()` | Get the current value | Primitives, counters |
| `instance()` | Get the underlying [Instance](/docs/liveobjects/concepts/instance) | Maps, counters |
| `compact()` | Get a JSON representation of the data | All paths |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite true, should be e.g. "Get a JavaScript object representation of the data"


#### Binary values <a id="compact-binary"/>

Binary values (such as `ArrayBuffer`) are returned as base64-encoded strings:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to implement what we discussed the other day - i.e. the compact method should either return a JSON serializable value which is identical to that returned by REST, or it should return a JS object with cyclic references and ArrayBuffer binary values.

Let me know if we need to sync on this. Changing the behaviour would be a breaking change post release, so I would suggest we tag the method experimental in the release (and label it as such here) before we do so.

```
</Code>

### Subscription options <a id="subscription-options"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should appear below subscribeIterator() method heading, and include an example of providing options for both styles of subscription API


Since PathObject resolves paths at runtime, it's important to understand what happens when a path doesn't resolve as expected.

### Access methods <a id="access-errors"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this include an example of using .instance() with a instanceof LiveMapInstance (or similar) to assert on the expected type at runtime?

link: '/docs/liveobjects/concepts/path-object',
},
{
name: 'Object Instance',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it should be Instance

@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 17, 2025 17:01 Inactive
@ably-ci ably-ci requested a deployment to ably-docs-ait-31-object-fvqfm5 December 17, 2025 17:01 Abandoned
@mschristensen mschristensen force-pushed the AIT-31/objects-path-api branch from e340a07 to 8f2ca12 Compare December 17, 2025 17:12
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 17, 2025 17:12 Inactive
@ably-ci ably-ci requested a deployment to ably-docs-ait-31-object-fvqfm5 December 17, 2025 17:12 Abandoned
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 17, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 17, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 17, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 17, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 17, 2025
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 18, 2025 09:27 Inactive
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 19, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 19, 2025
VeskeR added a commit to ably/ably-js that referenced this pull request Dec 19, 2025
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 17:50 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 17:54 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 18:05 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 18:16 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 18:18 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 18:50 Inactive

#### ObjectData

The `data` field in `mapOp` is an `ObjectData` object that represents the value assigned to a map key. It has the following properties:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above, ObjectData also represents the value for the intial entries in operation.map.entries

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah let's leave it for now, as we need to document subscriptions with the sync messages, if you could please create a ticket that would be grand

@mschristensen mschristensen force-pushed the AIT-31/objects-path-api branch from 611b43a to b6022a1 Compare December 19, 2025 18:56
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 18:56 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 19:00 Inactive
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 19:01 Inactive
Updates the LiveObjects product docs for the new path based API in
AblyJS, which is in public preview.

Swift, Java and REST API remain in Experimental.

Resolves AIT-31
@VeskeR VeskeR force-pushed the AIT-31/objects-path-api branch from 1e40f50 to b15caef Compare December 19, 2025 19:28
@ably-ci ably-ci temporarily deployed to ably-docs-ait-31-object-fvqfm5 December 19, 2025 19:28 Inactive
@mschristensen mschristensen enabled auto-merge (rebase) December 19, 2025 19:30
@mschristensen mschristensen merged commit 89fe085 into main Dec 19, 2025
7 checks passed
@mschristensen mschristensen deleted the AIT-31/objects-path-api branch December 19, 2025 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-app Create a Heroku review app

Development

Successfully merging this pull request may close these issues.

4 participants