Wednesday, 18 September, 2019 UTC


Summary

Let’s say we’re building a connected experience app with realtime features, something like chat messaging so users can text each other in-app. We’ve got great channel topology for global, group, and 1-to-1 chats. Chat messages are stored in PubNub History and can be retrieved later using the Storage & Playback API.
But what about our user and channel data? Where do we store those? Can we make changes to our data later? Looks like we’ll need to buy into some 3rd party database service and create a mutable storage system, because PubNub History provides infinite storage and playback for messages, but not for user information.
Not anymore!
At PubNub, we have developed Objects to get your user and chat room data storage resources built easily, in seconds – not weeks.
PubNub Objects
Objects allows applications to store and manage long-lived data for users and the channels they use to communicate within PubNub, without having to spin up additional servers. The features of Objects include:
  • Different schemas, like database tables, which are called Users, Spaces, and Memberships.
  • Relate Objects to one another with IDs, like a database table’s foreign key.
  • Long-lived data storage, unlike the PubNub Functions KV Store in which all data has a time-to-live (TTL).
  • Secure CRUD operations so only authorized users can control data changes with the help of PubNub Access Manager.
When to use Objects
Objects is inspired by chat use cases, but its functionality is not limited to chat. Any PubNub app that has unique users or devices can use Objects to manage groups like you would in any database. Unlike a traditional database, though, Objects is a serverless API.
Let’s say we are building a chat app like Slack. We need to enable users to join group chats. What is a design best-practice for our application to track which users are part of a group chat? How do we update our database when a new user is added to a group chat? Deleted from the group chat? Do we initialize these changes from the client or server side? Objects makes this simple.
Demo with Objects
Let’s go over a simple JavaScript demo to get a better understanding of Objects. For this demo, there are four columns that demonstrate the functionality of Objects:
  • create a user
  • create a space
  • add a user to a space
  • get a list of members for a space
When an operation is completed, like adding a user, the data will be displayed on the screen.
You can play around with the add, remove, and update functionality of Objects using this web browser demo. Here is a link to the Objects JS Demo Tool GitHub Repository.
You’ll need to put your PubNub Pub/Sub API keys into the JavaScript code (objects.js): all of the Objects that you write will be accessible with your API key set.
// Init PubNub
let pubnub = new PubNub({
    publishKey : 'INSERT_PUB_KEY',
    subscribeKey : 'INSERT_SUB_KEY',
    ssl: true
});
Open the HTML page in your favorite web browser and put your object data into the input fields. Hitting the Return key will trigger a write to Objects. Clicking the designated remove button will trigger a delete operation for the respective Object type.
This tutorial has three sections: Adding DataRemoving Data, and Fetching Data.
Creating Objects
Before we continue, if you don’t already have them you’ll need to sign up for a PubNub account to get your free API keys.  Use the form below to sign up or log in:
Once you have your free API keys, replace the publishKey and subscribeKey values in the following initialization code with your keys. This code is from objects.js. This JS file is where the demo code resides:
let channel = 'objects';

// Init PubNub
let pubnub = new PubNub({
  publishKey : 'INSERT_PUB_KEY',
  subscribeKey : 'INSERT_SUB_KEY',
  ssl: true
});

// Subscribe to a channel
pubnub.subscribe({
  channels: [channel]
});
Next, let’s create a user and save their information using Objects.

Create a User

When the user inputs their username and presses the Return key, a call to addUserInfo() is made. This is a PubNub JS SDK method that interacts with the API endpoints. Inside this method, a User Object is created by calling createUser(). The two required parameters for this method are the user_id and the user_name. The user_id must be unique, and is typically the user’s UUID. For the purposes of this demo, a random UUID is generated.
// Add user
addUserInfo = (e) => {
  // When user presses the 'Enter' key
  if((e.keyCode || e.charCode) === 13){
    // Generate random number for the user id
    let randNum = Math.floor(Math.random() * 50);
    let userId = `${userInput.value}-${randNum}`;
  }
}
We then make a call to the method createUser() with the random user ID and user input.
pubnub.createUser(
  {
    id: userId,
    name: userInput.value
  },
  function(status, response) {
    // Add to the beginning of users array
    users.unshift({
      user_id : userId,
      user_input : userInput.value
    });

    // Display on screen
    userBox.innerHTML =  (''+ userId)+ ': ' + (''+ userInput.value) +  '<br>' + userBox.innerHTML;
    userInput.value = '';
});
Once the user is added to the Object, the user ID and input are added to the front of the array called users. The array is useful for keeping track of the order in which the users were added. The most recent user is deleted when the Remove button is pressed. Also, the user_id is needed to remove a user from an Object. This will be explained further shortly.
Once a user is created, the user_id and user_name are displayed on the screen.
Let’s create a Space Object next.

Create a Shared Communication Space

Spaces represent things like chat rooms, categories for your IoT devices, or something similar, depending on your use case. The method to create a space object is createSpace(), where the two required parameters are space_id and the space_name. This method resides inside of addSpaceInfo(), which is called when the user inputs a space name and presses the Return key.  Since the space_id must be unique, the string ID is appended to the space_name. If a user tries to create a space that already exists, they will get an error from the API, which is reflected in the UI through an alert.
// Add space
addSpaceInfo = (e) => {
  if((e.keyCode || e.charCode) === 13){
    let spaceId = `${spaceInput.value}_id`;

    pubnub.createSpace(
      {
        id: spaceId,
        name: spaceInput.value
      },
      function(status, response) {
        if(status.error){
          alert('Error: A space with that name already exists')
          return;
        }
        spaces.unshift({
          space_id : spaceId,
          space_input : spaceInput.value
        });
        spaceBox.innerHTML =  (''+ spaceId)+ ': ' + (''+ spaceInput.value) + '<br>' + spaceBox.innerHTML;
        spaceInput.value = '';
      });
  }
}
Again, the data is added to the front of the array. The most recently made space is deleted when the Remove button is pressed. In order to delete a space, the space_id is required.
Once a space is created, the space_id and space_name are displayed on the screen.
Now that we have created the user and space Objects, let’s take a look at how to add a user to a space.

Add a User to a Space

To add a user to a space, we call the method joinSpaces() with parameters user_id and spaces. The user_id is the ID of the user to add to the space(s), while spaces is an array of objects containing the space(s) to be joined. The above method is inside of addUserToSpace() and is called when the user inputs a space ID and user ID and presses Return.  We check that neither input field is empty, as well as check that both IDs are valid.
addUserToSpace = (e) => {
  if((e.keyCode || e.charCode) === 13){
    // Check that both input fields are not empty
    if(memberInputSpacename.value && memberInputUsername.value){
      pubnub.joinSpaces(
        {
        userId: memberInputUsername.value, // Get user
        spaces: [
          {
            id: memberInputSpacename.value // Join to this space
          }
        ]
      },
      function(status, response) {
        if(status.error){
          alert('Error: Check that space-id and user-id is correct')
          return;
        }
        members.unshift({
          'space_id' : memberInputSpacename.value, 
          'user_id' : memberInputUsername.value,
        });
        memberBox.innerHTML =  (''+ memberInputSpacename.value) + ': ' + (''+ memberInputUsername.value) +'<br>' + memberBox.innerHTML;
        memberInputSpacename.value = '';
        memberInputUsername.value = '';  
      });
    } 
    else {
      alert('Enter both space and user field')
    }
  }
}
We also add the data to an array called members to delete the most recent data. The newly created membership is displayed on the screen with the space_id and the user_id.
Next let’s take a look at removing data from Objects.
Remove Data from Objects
With our demo tool, we can remove data using Objects and a straightforward user interface. Simply enter your record ID, and click the ‘Remove’ button. This will not only remove the data from the screen, but also delete the record from the Objects data store. You can confirm this by removing data with the tool and refreshing the web page. When the page re-loads it fetches all available Object entries. You’ll notice that the records you removed are no longer shown.
In the next section, we will cover deleting from Objects, including users, spaces and memberships.

Delete User Object

When the we click the ‘Remove’ button, the removeUser() method is called. As mentioned before, it removes only the most recent user in the list. In order to remove the user from Objects, we need the user_id. To get the ID, we do the following operation:
// Remove user
removeUser = () =>{
  //get user id from the first element of the array
  let userId = users[0]["user_id"];
}
Once we have the user_id, we call the method deleteUser() which removes the user from the Objects data store. The only parameter for this method is the user_id.
//remove user from user objects
pubnub.deleteUser(userId, function(status, response) { 
  console.log(response);
});
The rest of the code for removeUser() is the logic to remove the user from the browser screen.
// Remove the first element from the users array
users = users.slice(1);
userBox.innerHTML = '';

for(let x = users.length - 1; x >= 0; x--){ // start from end of array     
  let userId = users[x]['user_id']; // get user id
  let userValue = users[x]['user_input']; // get user value
  userBox.innerHTML =  (''+userId)+ ': ' + (''+userValue) +  '<br>' + userBox.innerHTML;
}

Delete Space Object

The code for removing a space from Objects is very similar to removing users. The only parameter we need to pass to deleteSpace() is the space_id.
// Remove space
removeSpace = () => {
   //get space id from the first element of the array
  let spaceId = spaces[0]['space_id'];
  //remove space from space objects
  pubnub.deleteSpace(spaceId, function(status, response) {
    console.log(response);
  });
}
To remove the space from the screen, we do the following:
// Remove the first element from the spaces array
spaces = spaces.slice(1);
spaceBox.innerHTML = '';

for(let x = spaces.length - 1; x >= 0; x--){ // start from end of array
  let spaceId = spaces[x]['space_id'];// get space id
  let spaceValue = spaces[x]['space_input']; // get space value
  spaceBox.innerHTML =  (''+spaceId)+ ': ' + (''+spaceValue) +  '<br>' + spaceBox.innerHTML;
}

Remove a User from a Space

In order to remove a user from a space with Objects, we need two parameters: user_id and spaces. The spaces parameter is a list of spaces from which to remove a user. These are the two required parameters for leaveSpaces().
// Remove user from a space
removeFromSpace = () => {
  //get user id from the first element of the array
  let userId = members[0]['user_id'];
  //get space id from the first element of the array
  let spaceId = members[0]['space_id'];
  
  // Remove user from space objects
  pubnub.leaveSpaces(
    {
      userId: userId,
      spaces: [
        spaceId
      ]
    },
    function(status, response) {
    }
  );
}
For this demo, we are removing the user from one space. Next, below the function(), add the following to update the screen.
// Remove the first element from the members array
members = members.slice(1);
memberBox.innerHTML = '';

for(let x = members.length - 1; x >= 0; x--){ // start from end of array
  let spaceId = members[x]['space_id']; // get space id
  let userId = members[x]['user_id']; // get user id
  memberBox.innerHTML = (''+ spaceId) + ': ' + (''+ userId) + '<br>' + memberBox.innerHTML;
}
We have covered two operations for Objects: Create and Delete. The last operation we’ll cover is Get.
Fetch Data from Objects
When a developer needs to fetch all records from a database, they use a SQL “select from” statement. Objects enables easy fetch functionality, so we can programmatically modify our user data in all scenarios.
When our Objects demo web page loads, data is fetched from the API, parsed, and displayed on the screen. This is possible because Objects data is long-lived. Next we will cover how to fetch data from Objects.

Get Members from a Space

We can retrieve members from a given space by using the SDK’s getMembers method. You need to provide a JavaScript object with the space ID (required) and a limiting number of members to be retrieved (optional). The second parameter is a callback function that will execute when Objects provides a response.
pubnub.getMembers({
    spaceId: spaceIdInput.value,
    limit: 10
},
function(status, response) {
    // ...
});

Get Users Object

The getUser method is similar. It requires a user ID.
pubnub.getUser({
        userId: userId
    },
    function(status, response) {
        // ...
    }
);

Get Spaces Object

We can use the getSpaces method to retrieve all spaces within with our API key set.
pubnub.getSpaces({
    limit: 5
},
function(status, response) {
    let spaceData = response.data;
    for (let x = 0; x < spaceData.length; x++) {
        let spaceId = spaceData[x]['id']; // get space id
        let spaceName = spaceData[x]['name']; // get space name

        spaces.unshift({ //add to spaces array
            space_id: spaceId,
            space_input: spaceName
        });

        // Display on screen
        spaceBox.innerHTML = ('' + spaceId) + ': ' + ('' + spaceName) + '<br>' + spaceBox.innerHTML;
    }
});

Get Users Membership Spaces

If we need to get all of the spaces that a user is a member of, we can use the getMemberships method. We need to provide a user ID to the method.
pubnub.getMemberships({
    userId: userId
},
function(status, response) {
    // Check if the user is a member of a space
    if (response.data.length > 0) {
        let membershipData = response.data;
        for (let x = 0; x < membershipData.length; x++) {
            let spaceNameId = membershipData[x]['id']; // Get userId

            members.unshift({ // Add to members array
                'space_id': spaceNameId,
                'user_id': userId,
            });
            // Display on screen
            memberBox.innerHTML = ('' + spaceNameId) + ': ' + ('' + userId) + '<br>' + memberBox.innerHTML;
        }
    }
});
Running the Objects Demo
To run the demo yourself, download the code from the GitHub repository. Then insert your free PubNub API keys into the JS code, as described above. Open the HTML file with your favorite web browser and try Objects for yourself!
Have suggestions or questions about the content of this post? Reach out at [email protected].
The post PubNub Objects: Long-Lived Data Storage for Realtime Apps appeared first on PubNub.