Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Go From Zero to Serverless with Backand, Part 3

DZone's Guide to

How to Go From Zero to Serverless with Backand, Part 3

In the third part of our series, we're going to look at using jQuery and JavaScript to configure and use the Backand SDK and create our UI.

· Web Dev Zone
Free Resource

Get deep insight into Node.js applications with real-time metrics, CPU profiling, and heap snapshots with N|Solid from NodeSource. Learn more.

In our last article (Part 2), we fleshed out the requirements for our application, built a data schema, and developed a relatively simple UI to manage our to-do list. With the preparation work done, we're finally ready to tie everything together. In this article, we'll take it a step further by adding in dynamic population functionality using JavaScript. We'll include the dependencies we need to get the project off the ground, look at configuring and using the Backand SDK, then we'll use jQuery to drive our UI population.

Adding Dependencies

As mentioned in our previous article, we'll be building out our UI using jQuery. This will impose some responsiveness limitations as we near the end of the project, but for the time being we should be able to make do, as our project is relatively simple. The primary way in which this will manifest is that there will be some hard-coding of HTML in the JavaScript code that, in a framework like React or Angular, would be handled for you. In the final article of this series, we'll look at some alternative UI frameworks that can be used to simplify this kind of effort.

In this project, we've already included one dependency - Twitter's Bootstrap - but we'll also need two more: jQuery, and the Backand SDK. jQuery will be used for dynamic population and manipulation of the Document Object Model (DOM), while the Backand SDK will provide our app with connectivity to the Backand application we created in the last part of this series. Let's start by modifying our header to pull jQuery from a CDN. jQuery provides their own CDN, nd gives you the capability to generate links to any of the available jQuery versions by visiting https://code.jquery.com/. We'll use the latest, 3.2.1:

<script 
    src="https://code.jquery.com/jquery-3.2.1.min.js"
    integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" 
    crossorigin="anonymous"></script>

(Note - I am using the minified version, to reduce the size of the end web page. You can use whichever version you find most beneficial)

Now, with jQuery included, we'll need to include the Backand SDK. Backand offers SDKs for multiple platforms. We'll be working with their "vanilla" SDK, which is designed to work with plain JavaScript applications. Instructions on including this in your project are available on the SDK's Github page. We'll just use the CDN, for simplicity's sake:

<script src="https://cdn.backand.net/vanilla-sdk/1.1.0/backand.min.js"> </script>

With this, our modified HTML header tag looks like the following:

<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
    <script src="https://cdn.backand.net/vanilla-sdk/1.1.0/backand.min.js"> </script>
</head>

A Note on CDNs and Dependency Managers

In the above section, we pull our dependencies from Content Delivery Networks, or CDNs. These have the benefit of allowing us to be certain that the file we are getting from the server is the exact version we are interested in. However, as your application grows, this approach to managing dependencies will start to introduce unnecessary bloat into your application. Using the CDN approach, any libraries that share dependencies will each maintain a separate copy of the dependency, meaning that your app will need to download two different versions of the same code in order to operate. This can give rise to several issues. For example, if two libraries rely upon differing versions of a dependency, they will both be downloaded, and can end up causing conflicts in the browser's JavaScript runtime. This can result in strange behavior that can be very challenging to pin down.

Dependency Managers are tools designed to resolve this problem. They analyze a list of JavaScript libraries requested by the application developer, and resolve the list of interdependency issues by locating compatible inclusion libraries. If two libraries rely on jQuery, for example, the package manager will determine the correct version of jQuery to include and download it to the developer's computer. This produces a reliable distribution that is not subject to the duplication issue present in blindly using CDNs, and gives other developers the peace of mind of knowing that they can recreate your app's execution environment exactly. There are a number of dependency managers available for JavaScript apps, such as Bower and Webpack, and each has a different network of available packages. While you'll need to do some work to ensure your desired libraries are available on the dependency manager's network, a little bit of legwork will save you a lot of bandwidth and debugging costs.

Pulling Results From Backand

Now that we've pulled in our JavaScript dependencies, it's time to connect to our Backand application! We'll start by defining our app's JavaScript in a new file, called app.js, and include it in our index.html.

Now, we define an object - dataService - that will maintain our app's connection to the Backand application:

var dataService = {
}

Within this object, we'll create a method, called init, that initializes the Backand SDK.

var dataService = {
  init: function(){
    backand.init(
      {
        appName: 'APP_NAME', 
        signUpToken: 'APP_SIGNUP_TOKEN',
        anonymousToken: 'APP_ANONYMOUS_TOKEN', 
        runSocket: false
      }
    );   
  }
}

This initializes the Backand SDK to communicate with an application. Each Backand application has three elements that are necessary for connecting to the platform. The first is an application's name, noted in the sample above as APP_NAME. This is given to the application when it is first created, and must be unique. The second item is the API Signup Token. The API Signup Token is used for API calls that manage your application's users, such as signup and socialSignup. This is available from the Backand app dashboard, under the "Security & Auth" menu, in the setion "Social & Keys":

Screen Shot 2017-04-11 at 1.05.13 PM.png

The final element is the Anonymous Access Token. This is used to authenticate API requests with your application when not operating within the context of an authenticated user. You can obtain this from the "Configuration" section of the "Security & Auth" panel in your application's Backand dashboard:

Screen Shot 2017-04-11 at 1.08.03 PM.png

The relevant section, titled "Anonymous Access," has several important settings. The first is a toggle switch, on the right side of the page, that enables anonymous access to your application. If this is disabled, then all users must log in to your application prior to contacting the API successfully. The second is a security role - we won't cover that in this tutorial, but it uses Backand Security Roles to control the access that anonymous users have to your application. The final setting is the anonymous access token itself, which you will need to copy into your application (replacing 'APP_ANONYMOUS_TOKEN'):

anonymous_Section_key_blurred.png

Now that we have our app's name, API signup token, and anonymous access token, we're ready to work with Backand! Backand is implemented as a RESTful web API, built on top of your app's database and manipulated via HTTP requests. The Backand SDK wraps these HTTP requests for you, taking over the complexity of generating the URL to be contacted and constructing the authentication headers needed based on the application's authentication state. This means instead of sending an explicit GET request to an API endpoint, we can use code similar to the following to fetch a list of "item" objects from the server:

backand.object.getList("items").then(...);

All Backand SDK methods return promises, which need to be resolved before being used to populate any internal application state or update user interfaces. This means that our code will require two items: a method to call the SDK, and a method to handle the response. We'll call these getData and getDataSuccess. We'll also add an internal variable -itemData - that we'll use to store the data obtained from Backand. The updated dataService object looks like this:

var dataService = {
  init: function() {
    backand.init(
      {
        appName: 'APP_NAME', 
        signUpToken: 'APP_SIGNUP_TOKEN',
        anonymousToken: 'APP_ANONYMOUS_TOKEN', 
        runSocket: false
      }
    );
    this.itemData = null;
  },
  getData: function() {
    backand.object.getList("items").then(this.getDataSuccess);
  },
  getDataSuccess: function(response) {
    this.itemData = response.data;
  }
}

An important note: Backand provides sample code in the Backand dashboard for all SDK operations. For example, if you have an "Items" object in your database, you'll see the following section of the UI on the right hand side of the object's "data" tab:

Screen Shot 2017-04-11 at 1.26.57 PM.png

This provides the API URL for the object, a sample code to perform a getList() call against that endpoint, and a sample response you can use to structure your application's code. The power of this tool appears, though, after you've made a few modifications to the data grid. As you perform edits to the data grid, Backand is conducting the underlying operations via API calls. These API calls are compiled into a list that you can view, by clicking on the drop-down menu next to the request description:

Screen Shot 2017-04-11 at 1.29.21 PM.png

Updating Our UI

With our data service created, and tied into our application's back end, we only have a couple steps left to update our UI with data from the server. First, we'll need a trigger we can use to contact our data service, allowing us to obtain the list from the server. We'll define a trigger on the $document.ready event, which is fired when a page has finished loading in the browser. In app.js, underneath our dataService object definition, we'll define the event handler as follows:

$(document).ready( function() {
  // handler code goes here
});

Within this function, we'll initialize our dataService object, and contact Backand to get the initial list:

$(document).ready( function() {
  // init data service
  dataService.init();
  // populate the data service from the server
  dataService.getData();
});

Now we run up against a problem in our application's design. The data is populated internally within dataService as the result of an asynchronous call to getDataSuccess(). However, if we try to return the data from the getData() call, then we will not have the correct data due to the SDK call being asynchronous. There are a couple potential solutions to this:

  1. Implement the success handler outside of the dataService class, and return the promise object from the getData function.

  2. Transmit an event upon completion of the SDK call, indicating to the observer that fresh data is available for consumption.

Which approach you choose is going to be largely dependent upon your application's architecture. I will be following the second approach, since it does a better job of maintaining data encapsulation at the expense of some additional usage complexity. We'll define a new event type, dataServiceUpdated, and fire it from the success handler within the data service:

var dataService = {
  // other functions above...
  getDataSuccess: function(response) {
    this.itemData = response;
    // Emit an alert to the app, triggering the data refresh
    $("body").trigger("dataServiceUpdated", this.itemData);
  }
}

Next, we need to write an event handler and subscribe to this new event. We'll call this dataServiceUpdatedHandler, and implement it as follows:

var dataServiceUpdatedHandler = function(event, itemData) {

};
$("body").on("dataServiceUpdated", dataServiceUpdatedHandler);

This creates a new function, dataServiceUpdatedHandler, which will be called upon completion of the SDK call to Backand. We can use this in conjunction with the event handler's ability to pass data with events. You'll notice that in the success handler, we supplied this.itemData as the second argument to the trigger function - this passes the provided data through as an argument to any event handler.

Now that we can pull the data out, we'll update our dataServiceUpdatedHandler function to store this data for processing. We'll do this by extrating the data member of the response, and sending it to another function, formatItemListHTML:

var dataServiceUpdatedHandler = function(event, itemData) {
  // Populate HTML content
  formatItemListHTML(itemData.data);
};

The formatItemListHTML function will take this list, and convert it into HTML that we can add to our page using jQuery. First, we'll copy the relevant components of index.html. We define an item entry in the list using the following HTML:

<a href="#" class="list-group-item list-group-item-action">
    <div class="d-flex w-100 justify-content-between">
        <h5 class="mb-1">To Do Item 2</h5>
        <button type="button" class="btn btn-success">Expand</button>
    </div>
</a>

I've selected one of the simpler item tags, rather than including the description - we'll expand this code to include the description in a future article in this series. Using this as a template, we can easily cycle through our list and create a set of anchor tags that contain content built from our server-side:

var formatItemListHTML = function(itemList) {
  const ITEM_TAG = "<a href='#' class='list-group-item list-group-item-action'>"
                    + "<div class='d-flex w-100 justify-content-between'>"
                    + "<h5 class='mb-1'>ITEM_NAME</h5>"
                    + "<button type='button' class='btn btn-success'>Expand</button>"
                    + "</div></a>";

  var length = itemList.length;
  var finalHtml = [];

  for( var i = 0; i < length; i++ ) {
    finalHtml.push(ITEM_TAG.replace('ITEM_NAME', itemList[i].name));
  }

  return finalHtml.join("");
};

Now that we've been able to successfully build out the wiring for our app, we have a couple minor modifications to make to our app's HTML. Start by adding an ID - itemList - to the div containing our to-do list. Then, we'll remove the stub data so that we can see the server linkage in action:

<div class="list-group" id="itemList">

</div>

Finally, we're ready to tie everything together. Add one final line to the data updated handler to modify this div's inner HTML, and we're set to go:

var dataServiceUpdatedHandler = function(event, itemData) {
  // Populate HTML content
  itemHTML = formatItemListHTML(itemData.data);

  $("#itemList").html(itemHTML);
};

With that, save your work, open the page in the browser, and you should see…

Screen Shot 2017-04-11 at 3.02.49 PM.png

While it may seem as though we haven't wired things together properly, it's important to remember that you're now working with an app that has a fully-implemented back end - and that back end has no data! Open up the backand dashboard, open your app, then add some items to your app using the Data tab of the Items object:

Screen Shot 2017-04-11 at 3.05.34 PM.png


Once you've done that, refresh your app's page to see the successful result:


Screen Shot 2017-04-11 at 3.20.52 PM.png

For reference, here is the current content of index.html:

<html>
  <head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
    <script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
    <script src="https://cdn.backand.net/vanilla-sdk/1.1.0/backand.min.js"> </script>
    <script src="scripts/app.js"> </script>
  </head>
  <body>
    <h1 align="center"> To Do List </h1>
    <div class="list-group" id="itemList">

    </div>
  </body>
</html>

And scripts/app.js:

var dataService = {
  init: function() {
    backand.init(
      {
        appName: 'APP_NAME',
        signUpToken: 'API_SIGNUP_TOKEN',
        anonymousToken: 'ANONYMOUS_TOKEN',
        runSocket: false
      }
    );
    this.itemData = null;
  },
  getData: function() {
    backand.object.getList("items").then(this.getDataSuccess);
  },
  getDataSuccess: function(response) {
    this.itemData = response;
    // Emit an alert to the app, triggering the data refresh
    $(document).trigger("dataServiceUpdated", this.itemData);
  }
};

var formatItemListHTML = function(itemList) {
  const ITEM_TAG = "<a href='#' class='list-group-item list-group-item-action'>"
                    + "<div class='d-flex w-100 justify-content-between'>"
                    + "<h5 class='mb-1'>ITEM_NAME</h5>"
                    + "<button type='button' class='btn btn-success'>Expand</button>"
                    + "</div></a>";
  var length = itemList.length;
  var finalHtml = [];

  for( var i = 0; i < length; i++ ) {
    finalHtml.push(ITEM_TAG.replace('ITEM_NAME', itemList[i].name));
  }
  return finalHtml.join("");
};


var dataServiceUpdatedHandler = function(event, itemData) {
  // Populate HTML content
  itemHTML = formatItemListHTML(itemData.data);

  $("#itemList").html(itemHTML);
};

$(document).on("dataServiceUpdated", dataServiceUpdatedHandler);

$(document).ready( function() {
  // init data service
  dataService.init();

  // populate the data service from the server
  dataService.getData();
});

Conclusion and Next Steps

Now that we have tied our app up to a server, the world is quite literally our oyster. At the moment we're only demonstrating a simple object retrieval using the Backand SDK, but there is a lot more power available. In the next article, we'll look at creating, updating, and deleting data in addition to simple retrieval. We'll also take a look at user authentication, and then start exploring tools for improving our application. For now, use the code above as a template for working with Backand - the documentation has several examples on how to effectively leverage the Backand SDK to reach your goals.

Node.js application metrics sent directly to any statsd-compliant system. Get N|Solid

Topics:
serverless ,jquery ,javascript ,backand ,html ,web dev

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}