Tutorial: Migrating to API 3.X

JavaScript Migrating to API 3.X

Webcom SDK for JavaScript version 3.X provides a new API style.

The main innovation is a clear separation between a Webcom application and the Webcom services available on this application.

Using the 3.X API

1. Create an instance of WebcomApp
const app = Webcom.App("<your-app>");

Each instance of WebcomApp manages exactly one instance of each available Webcom service through dedicated properties:

Each service is actually initialized at the first access to its corresponding getter. For example, if you only use the authentication property of your WebcomApp instance, its Serverless Database service will never be initialized, and so its associated websocket will never be opened.

Also, configuration data may be passed to each Webcom service through the second argument of the Webcom.App factory function:

const app = Webcom.App("<your-app>", {
    ServerlessDb: {connect: false}, // see /doc/sdk/#webcom-sdk-js?/ServerlessDb.html#.Configuration
    Authentication: {storage: "myAuthKey"}, // see /doc/sdk/#webcom-sdk-js?/Authentication.html#.Configuration

⚠ Warning ⚠

Be aware that each instance of WebcomApp manages its own instances of Authentication and Serverless Database instances!
As a consequence, an application should create only one instance of WebcomApp, otherwise it is likely to manage several Serverless Database instances, and so to open several websockets to the Webcom back end, which is generally not recommended: see details below.

2. Using the Authentication service

The name of each sign-in method is prefixed with signIn (instead of auth or authWith in API 2.X):

    .then(authDetails => console.log("Signed-in with", auth.provider, "as", authDetails.uid))
    .catch(error => console.log("Could not sign in:", error.message));

The sign-out method is signOut() instead of logout():

app.authentication.signOut().then(() => console.log("Signed out!"));

All other authentication methods (available on 2.X Webcom instances) remain available on the 3.X app.authentication object. For example:

    .then(() => console.log("Identity removed!"))
    .catch(error => console.log("Failed to remove identity:", error.message));
3. Using the Serverless Database service

The main changes apply to the Serverless Database service.
General operations are directly available on the serverlessDb property (and are roughly the same as the ones available on the deprecated datasync property of the Webcom class). For example:

app.serverlessDb.connect(); // establish the websocket of the Serverless Database service
app.serverlessDb.disconnect(); // shuts the websocket down

Whereas operations related to data nodes (reading, writing, subscribing) need to get first a ServerlessDbNode instance. It can be retrieved using the rootNode property, combined with the relativeNode method:

const node = db.rootNode.relativeNode("/the/node/path");

For example, subscribing to the addition of new children on this node can be done as following:

const callback = Webcom.Callback(s => console.log("New child", s.node.key, "with value", s.val()))
let subscription;
node.subscribe(Webcom.Event.Child.Addition, callback)
    .then(s => subscription = s)
    .catch(error => "Subscription failed:", error.message);

While the corresponding unsubscription is implemented as either:




Migrating from the 2.X API to the 3.X API

The following table gives a wide (although not exhaustive) correspondence between API 2.X calls and API 3.X ones:

 Initialization operations
const ref = new Webcom("<your-app"); const app = Webcom.App("<your-app>");see below
const db = app.serverlessDb;
const auth = app.authentication;
const ref = new Webcom("<your-app", config); const app = Webcom.App("<your-app>", config);see below
const db = app.serverlessDb;
const auth = app.authentication;
 Browsing operations
const node = ref.child("sub/path"); const node = db.rootNode.relativeNode("sub/path");
node = node.parent(); node = node.parent;
const name = node.name(); const name = node.key;
const path = node.pathString(); const path = node.path;
 Writing operations
node.set("value"); node.set("value");
node.onDisconnect().set("value"); node.set("value", Webcom.NEXT_DISCONNECTION);
node.onDisconnect().cancel(); node.cancelNextDisconnectionOps();
node = node.push("value"); node.push("value").then(key => node = node.relativeNode(key));
const time = Webcom.pushIdToDateTime(node.name()); const time = node.timestamp.valueOf();
const uniqueKey = node.push().name(); const uniqueKey = db.generateUniqueKey();
 Reading operations
node.get().then(s => console.log(s.val())); node.get().then(s => console.log(s.val()));
console.log(node.getCache().val()); console.log(node.getCache().val());
node.startAt("h").get().then(s => console.log(s.val())); node.get(Webcom.Constraint.StartAt("h")).then(s => console.log(s.val()));
 Subscription operations with callbacks
node.on(["value","value_ack"], s => console.log(s.val())); const callback = Webcom.Callback(s => console.log(s.val())).includeAcknowledgements();
node.subscribe(Webcom.Event.ValueChange, callback);
node.on("value", s => console.log(s.val()), () => Webcom.KEEP); const callback = Webcom.Callback(s => console.log(s.val()), Webcom.KEEP);
node.subscribe(Webcom.Event.ValueChange, callback);
const callback = node.limit(2).on("value", s => console.log(s.val())); const callback = Webcom.Callback(s => console.log(s.val()));
node.subscribe(Webcom.Event.ValueChange, callback, Webcom.Constraint.Last(2));
node.off("value", callback); node.unsubscribe(callback);
node.off(); not available
 Subscription operations with webhooks
const webhook = Webcom.Webhook("id","context");
node.subscribe(Webcom.Event.ValueChange, webhook);
node.unsubscribe(webhook, true); unchanged
 Connection operations
const dbState = ref.datasync.currentState; const dbState = db.currentState;
console.log(ref.datasync.shouldBeConnected ? "should" : "should not", "be connected"); console.log(db.shouldBeConnected ? "should" : "should not", "be connected");
ref.datasync.connect(); db.connect();
ref.datasync.disconnect(); db.disconnect();
 Cache-related operations
const data = ref.datasync.offlineData; const data = db.offlineData;
ref.datasync.persist(); db.persist();
ref.datasync.cleanOfflineData(); db.cleanOfflineData();
 Authentication operations
ref.addAuthCallback((err, auth) => { ... }); auth.subscribe((err, state) => { const auth = state.details; ... }
ref.logout(); auth.signOut();
ref.authAnonymously().then(authDetails => console.log(authDetails)); auth.signInAsGuest().then(authDetails => console.log(authDetails));
ref.authInternally("password", {id:"foo@bar.com", password:"123"}); auth.signInWithCredentials("password", {id:"foo@bar.com", password:"123"});
any other ref.authWithXXX(...) method auth.signInWithXXX(...) method
any other ref.XXX(...) authentication-related method auth.XXX(...) method


1. WebcomApp should be instantiated only once

In v2.X a Webcom instance was a ref obtained via a 'ref = new Webcom(...)', it was “the entry point for all classes, functions and properties of the Webcom SDKbut also a reference on a data node. So working with multiples nodes of a same database could lead to instantiate the Webcom class multiple times, one time for each node, which was not an issue and was considered as a “normal way of working”.

In v3.X, on the contrary, the WebcomApp class should be instantiated only once. Such an instance is built by invoking the App property of the Webcom object. It owns a unique serverlessDb property that manages the ServerlessDb Service, which in turns manages the websocket to the Webcom back end. Instantiating several WebcomApp objects leads to open several websocket to the back end, which is generally not recommended (except in specific cases, see below).

With this new API, accessing to different nodes is then possible via the rootNode property of the serverlessDb service and then the relativeNode() method of the obtained ServerlessDbNodeobject. If you need working with several database nodes, then invoke several times the rootNode property to get several ServerlessDbNode instances, but invoke the Webcom.App() method only once and share the obtained WebcomApp instance all across your JavaScript application.

Let's illustrate this in an example:

// connecting to my app
const app = Webcom.App("myPersonalApp");  // creates a WebcomApp instance, to be shared all across the JS application
const auth = app.authentication; // get the Authentication Service from the shared WebcomApp object
const database = app.ServerlessDb; // get the ServerlessDb Service from the shared WebcomApp object, this opens the websocket
// then authenticating
auth.signInWithCredentials({email: "me@orange.com", password: "myPassword"});
// then making many things... 
// ... 
// then, later, accessing to one of my user in database
let user, node;
user = "John";
node = database.rooNode.relativeNode(`/${user}`); // create a new node from the ServerlessDb Service
// making some stuff with this user...
// then moving to another user
user = "Paul";
node = database.rooNode.relativeNode(`/${user}`); // create a new node from the ServerlessDb Service
// making some stuff with this user...
2. Specific case where to create many instances of WebcomApp

Managing two different authentication services on the same WebcomApp instance is not possible. If needed you can create two separate instances of this same application, each of them linked to a different authentication service:

const appWithAuthent1 = Webcom.App("myApp"); // get a first instance of WebcomApp to "myApp"
const auth1 = appWithAuthent1.authentication; // get the Authentication Service of instance #1
auth1.signInWithCredentials({email: "me@orange.com", password: "myPassword"});
const database1 = appWithAuthent1.serverlessDb; // get the ServerlessDb Service of instance #1,
                                                // which opens a websocket authenticated with auth1

const appWithAuthent2 = Webcom.App("myApp"); // get a second instance of WebcomApp to "myApp"
const auth2 = appWithAuthent2.authentication; // get the Authentication Service of instance #2
const database2 = appWithAuthent2.serverlessDb; // get the ServerlessDb Service of instance #2,
                                                // which opens another websocket authenticated with auth2

// then database1 is the root point to work on database with auth1
//  and database2 is the root point to work on database with auth2
//  ==> this results in 2 websockets to the back end, one for each authentication