Sunday, February 22, 2015

After the AngularJS Tutorial

I have another series called "After the AngularJS Tutorial"  <http://aftertutorial.blogspot.com/> that covers a number of more advanced AngularJS topics that have come up as I developed a number of Single Page Apps (SPAs).

Wednesday, January 21, 2015

Wrapping Up This Series

At this point, we have touched upon all the key elements to get a functioning SPA working. The final version of this application can be found at <http://jsfiddle.net/sckmkny/y0Lfo7yt/>.

A more complete deployment would also include the following elements:
  • Handling unexpected errors; notice that we had left most of the error handlers empty.  Because these errors are unexpected, one reasonable choice is to send the user to an error page with descriptive information.  The only action would be to reload the application, i.e., setting the location.href of the JavaScript window object (accessible through $window in Angular.js.
  • Fleshing out the rest of the app; e.g. Wines and the editing elements.  While this is a non-trivial amount of work, it is essentially repeating a combination of elements that were already covered.
  • Blocking the UI when waiting on a return from an asynchronous function, e.g., saving data.  One can use a module like angular-block-ui <https://github.com/McNull/angular-block-ui>.
  • Securing the Firebase API. This is fairly simple and well documented at <https://www.firebase.com/docs/security/guide/index.html>.
  • Breaking out the app into the component files (again in JSFIDDLE we were limited to a single file). 
  • On a similar note, downloading and using a specific version of the required libraries / modules, e.g., Bootstrap, Angular.js, etc.
  • Using the HTML5 Application Cache <http://www.w3schools.com/htmL/html5_app_cache.asp> to speed up the loading of the app.
A more complete example of a solution (running in production) can be found at <https://github.com/larkintuckerllc/pcwl>.  The running application can be run from <https://pcwl.firebaseapp.com>.


Adding Authentication (and a Sidebar into Form Validation)

One of the nice features of Firebase is that they provide a simple but robust mechanism to handle authentication (and authorization). To get started with these features, one logs into the Firebase dashboard for their app and select the "Login & Auth" tab and:
  1. Under the "Email & Password" tab, check the "Enable Email & Password Authentication".
  2. At the bottom of the screen, add a new user.
note: To see the newly created user under "Registered Users", reload the screen (this seems to be a bug).

While there a mechanism to add new users from the SPA, for this simple example we will only create a login screen to support authenticating.

The first step is to add a Login and Logout option to the home page view (say as list items right above the Wineries and Wines) list items.  The intent is to show the Login option when not authenticated and the Logout option when authenticated.

HTML
 
            <ul class="list-group">
                <li ng-if="! authenticated" ng-click="navigate('/login');" class="list-group-item">Login</li>
                <li ng-if="authenticated" ng-click="logout();" class="list-group-item">Logout</li>
                <li ng-click="navigate('/wineries');" class="list-group-item">Wineries</li>
                <li ng-click="navigate('/wines');" class="list-group-item">Wines</li>
            </ul>

Then one needs to update the home screen controller with some familiar code and two new Firebase functions getAuth() and unauth(); documented at <https://www.firebase.com/docs/web/guide/user-auth.html>.

JavaScript
controllers.controller('HomeCntrl', ['$scope', '$location', '$window', function($scope, $location, $window) {
    var myDataRef;
    $scope.navigate = function(path) {
        $location.path(path);
    };
    $scope.authenticated = false;
    myDataRef = new $window.Firebase('https://wineapp.firebaseio.com');
    $scope.logout = function() {
        $scope.authenticated = false;
        myDataRef.unauth();
    };
    var authData = myDataRef.getAuth();
 if (authData != null) {
        $scope.authenticated = true;
    }
}]);

Next we, create a login screen first by adding the routes to the routes (say just after the "/" route):

JavaScript
            when('/login', {
                   templateUrl: 'views/login.html',
                    controller: 'LoginCntrl'
            }).

Next we create the login screen view (say just below the home page view). The new concepts introduced here are:

HTML
 
 <!-- /views/login.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/login.html">      
        <div class="panel panel-default">
            <div class="panel-heading">
                 <h3 class="panel-title"><button ng-click="navigate('/');" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span> Back</button> Login</h3>                
            </div>
            <div class="panel-body">
                <form name="login_form" role="form" novalidate>
                    <div ng-class="{'form-group': true, 'has-feedback': true, 'has-error': login_form.email.$invalid}">
                        <label class="control-label" for="email">Email</label>
                        <input type="email" name="email" class="form-control" id="email" placeholder="Email" ng-model="email" required />
                    </div>
                    <div ng-class="{'form-group': true, 'has-feedback': true, 'has-error': login_form.password.$invalid}">
                        <label class="control-label" for="password">Password</label>
                        <input type="password" name="password" class="form-control" id="password" placeholder="Password" ng-model="password" required />
                    </div>
                </form>
                <p ng-if="failed" style="text-align: center" class="text-danger">email or password incorrect</p> 
            </div>
            <div class="panel-footer">
                <button ng-click="login();" type="button" class="btn btn-default btn-sm" ng-disabled="login_form.$invalid" ><span class="glyphicon glyphicon-user"></span> Login</button>
            </div>
        </div>
    </script>
    <!-- EOF -->    

Lastly we need to add in the login screen controller:

JavaScript
controllers.controller('LoginCntrl', ['$scope', '$location', '$window', '$timeout', function($scope, $location, $window, $timeout) {
    var myDataRef;
    $scope.navigate = function(path) {
        $location.path(path);
    };
    $scope.failed = false;
    myDataRef = new $window.Firebase('https://wineapp.firebaseio.com');
    $scope.login = function() {
        myDataRef.authWithPassword({
            email: $scope.email,
            password: $scope.password
        }, function(error, authData) {
            $timeout(function() {   
                if (! error) {
                    $scope.navigate('/');
                } else {
                    $scope.failed = true;
                    $scope.password = '';
                }
            });
        });;
    };
}]);

Sunday, January 18, 2015

Mixing in the Model

We now mix in the model from an earlier post to pull in a list of the wineries on the "Wineries" screen. The key additional concepts that are introduced are:
note: There is one bit of complicated JavaScript that converts the snapshot.val() into the $scope.wineries array.

Replace the respective sections of the JSFIDDLE example as described in <http://buildspa.blogspot.com/2015/01/mixing-in-view.html>.

HTML
    <!-- /views/wineries.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/wineries.html">
        <div class="panel panel-default">
            <div class="panel-heading">
                 <h3 class="panel-title"><button ng-click="navigate('/');" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span> Back</button> Wineries</h3>
            </div>
            <ul class="list-group">
                <li ng-repeat="winery in wineries" class="list-group-item">
                   {{winery.val.name}}
                </li>
            </ul>
        </div>
    </script>
    <!-- EOF -->

JavaScript
controllers.controller('WineriesCntrl', ['$scope', '$location', '$window', '$timeout', function($scope, $location, $window, $timeout) {                       
    var myDataRef;
    $scope.wineries;
    $scope.navigate = function(path) {
        $location.path(path);        
    };
    myDataRef = new $window.Firebase('https://wineapp.firebaseio.com');
    myDataRef.child('wineries').once('value', function(snapshot) {
        $timeout(function() {
            $scope.wineries = Object.keys(snapshot.val()).map(function(key) {
                return {key: key, val: snapshot.val()[key]};
            });
        });
    }, function(error) {
    });
}]);

Mixing in the View

Now that we have the controller in place, we now are going to bring in the view.  In addition to the Bootstrap panel from an earlier post, we use:
Like the previous example in JSFIDDLE, we will use the version of Angular.js provided by a Google CDN
<//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js> by adding it to the "External Resources". Likewise will be using a Google CDN version of ngRoute <//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-route.min.js>.

We also need to bring back in Bootstrap 3.2.0; in JSFIDDLE select jQuery 2.1.0 (or greater) in the Frameworks & Extensions and then select Bootstrap 3.2.0.

note: Actually, jQuery 2.1.0 is not necessary for this application (but included it because it is the easiest way of adding Bootstrap in JSFIDDLE).

note: It is important to set the place the JavaScript included in the HTML as "No wrap - in <head>".

For the remaining posts in this blog, we will be building onto this example (so one needs to save this example in JSFIDDLE).

HTML
<!-- index.html -->
<!-- normally files are stored in separate files; need to src js
<script src="controllers/my_controllers.js"></script>
<script src="app.js"></script>
<script src="routes/routes.js"></script>
-->
<div ng-app="myApp">
   <div ng-view></div>
    
    <!-- /views/home.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/home.html">
        <div class="panel panel-default">
            <div class="panel-heading">
                <h3 class="panel-title">Home</h3>
            </div>
            <ul class="list-group">
                <li ng-click="navigate('/wineries');" class="list-group-item">Wineries</li>
                <li ng-click="navigate('/wines');" class="list-group-item">Wines</li>
            </ul>
        </div>
    </script>
    <!-- EOF -->
    
    <!-- /views/wineries.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/wineries.html">
        <div class="panel panel-default">
            <div class="panel-heading">
                 <h3 class="panel-title"><button ng-click="navigate('/');" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span> Back</button> Wineries</h3>
            </div>
            <ul class="list-group">
            </ul>
        </div>
    </script>
    <!-- EOF -->
    
    <!-- /views/wines.html (obmit work-around script tag) -->
    <script type="text/ng-template" id="views/wines.html">
        <div class="panel panel-default">
            <div class="panel-heading">
                 <h3 class="panel-title"><button ng-click="navigate('/');" class="btn btn-default"><span class="glyphicon glyphicon-chevron-left"></span>  Back</button> Wines</h3>
            </div>
            <ul class="list-group">
            </ul>
        </div>
    </script>
    <!-- EOF -->
    
</div>
<!-- EOF -->

JavaScript
// controllers/my_controllers.js
var controllers = angular.module('myControllers', []);
controllers.controller('HomeCntrl', ['$scope', '$location', function($scope, $location) {
    $scope.navigate = function(path) {
        $location.path(path);
    };
}]);
controllers.controller('WineriesCntrl', ['$scope', '$location', function($scope, $location) {                       
    $scope.navigate = function(path) {
        $location.path(path);
    };
}]);
controllers.controller('WinesCntrl', ['$scope', '$location', function($scope, $location) {
    $scope.navigate = function(path) {
        $location.path(path);
    };
}]);
// EOF

// app.js
var myApp = angular.module('myApp', [
    'ngRoute',
    'myControllers'    
]);
// EOF

// routes/routes.js
angular.module('myApp').config(['$routeProvider',    
    function($routeProvider) {
        $routeProvider.
            when('/', {
                    templateUrl: 'views/home.html',
                    controller: 'HomeCntrl'
            }).
            when('/wineries', {
                    templateUrl: 'views/wineries.html',
                    controller: 'WineriesCntrl'
            }).
            when('/wines', {
                    templateUrl: 'views/wines.html',
                    controller: 'WinesCntrl'
            }).
            otherwise({
                redirectTo: '/'
            });
    }
]);
// EOF

HTML (in the CSS Section)
</style>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>

Saturday, January 17, 2015

Multiple Screens in the SPA

Angular uses different hash URLs, e.g., <http://domain.com/#/> and <http://domain.com/#/login> to differentiate screens as one navigates the app.  Like any URL they can be bookmarked and crawled independently by the search engines. The important thing to understand is that the browser is not reloading the page as it navigates to a different hash URL.

Angular uses a separate ngRoute module to handle navigation between screens within an app. In order to make sense of the following, one will need to make it through step 7 in the tutorial: Routing & Multiple Views <https://docs.angularjs.org/tutorial/step_07>.

Like the earlier example in JSFIDDLE, we will use the version of Angular.js provided by a Google CDN
<//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js> by adding it to the "External Resources". Likewise will be using a Google CDN version of ngRoute <//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular-route.min.js>.

note: It is important to set the place the JavaScript included in the HTML as "No wrap - in <head>".

Because JSFIDDLE only has a single HTML file with a single block of embedded JavaScript, we will collapse what is normally in separate files into the single HTML file. The normal file structure would be (see comments in the example):

note: Having the application split across multiple files helps with the performance as well makes it a bit easier to develop.  For example, page1.html is not loaded until it is needed.

  • index.html
  • app.js
  • controllers
    • my_controllers.js
  • routes
    • routes.js
  • views
    • home.html
    • page1.html
    • page2.html

HTML
<!-- normally files are stored in separate files
<script src="controllers/my_controllers.js"></script>
<script src="app.js"></script>
<script src="routes/routes.js"></script>
-->
<div ng-app="myApp">
   <div ng-view></div>
    
    <!-- SEPARATE FILE views/home.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/home.html">
        Home<br />
        {{test}}<br />
        <br />
        <a href="#/p1">Page 1</a><br />
        <a href="#/p2">Page 2</a><br />
    </script>
    <!-- EOF -->
    
    <!-- SEPARATE FILE views/page1.html (obmit work-around script tag)-->
    <script type="text/ng-template" id="views/page1.html">
        Page 1<br />
        {{test}}<br />
        <br />
        <a href="#/">Back Home</a><br />
    </script>
    <!-- EOF -->
    
    <!-- SEPARATE FILE views/page2.html (obmit work-around script tag) -->
    <script type="text/ng-template" id="views/page2.html">
        Page 2<br />
        {{test}}<br />
        <br />
        <a href="#/">Back Home</a><br />
    </script>
    <!-- EOF -->
    
</div>

JavaScript
// SEPARATE FILE controllers/my_controllers.js
var controllers = angular.module('myControllers', []);
controllers.controller('HomeCntrl', ['$scope', function($scope) {
    $scope.test = 'hello world';
}]);
controllers.controller('Page1Cntrl', ['$scope', function($scope) {
    $scope.test = 'wow this worked';
}]);
controllers.controller('Page2Cntrl', ['$scope', function($scope) {
    $scope.test = 'this is cool';
}]);
// EOF

// SEPARATE FILE app.js
var myApp = angular.module('myApp', [
    'ngRoute',
    'myControllers'    
]);
// EOF

// SEPARATE FILE routes/routes.js
angular.module('myApp').config(['$routeProvider',    
    function($routeProvider) {
        $routeProvider.
            when('/', {
                    templateUrl: 'views/home.html',
                    controller: 'HomeCntrl'
            }).
            when('/p1', {
                    templateUrl: 'views/page1.html',
                    controller: 'Page1Cntrl'
            }).
            when('/p2', {
                    templateUrl: 'views/page2.html',
                    controller: 'Page2Cntrl'
            }).
            otherwise({
                redirectTo: '/'
            });
    }
]);
// EOF

Sidebar Bit on Other Server Frameworks

In these series of posts we have been working with Firebase as the database and application server. While Firebase does a lot of the application server functions well, e.g., providing a CRUD API, authentication, authorization, and even validation, at this point it cannot perform any additional operations, e.g., get or send data through a third-party API.  It is my understanding this is what they are working on through their acquisition by Google.

note: Referential integrity within Firebase can be maintained using a combination of validation rules and use of the onDisconnect feature (more on that later).

As this is a serious limitation (really do not want the client to have to do all the work; esp. if the integrity of the data is at stake), one has to consider alternative solutions to perform these sorts of operations.

I have explored two modern server frameworks (Ruby on Rails and Node.js / Express) and here are my observations:

Ruby on Rails

+ Popular with Developers
+ Popular with Clients
+ Opinionated; i.e., with all the generators and such there is a prescribed way of doing things.

- Ruby (I already know JavaScript from the client; context switching to Ruby is a pain).
- Opinionated;  i.e., there is a quite a bit to learn to get started.
- Generators; I find code that generates code distasteful.
- Overkill: Much of the Ruby on Rails examples assume that one is writing a server-side application that needs to handle things like sessions and HTML generation.

Node.js  / Express

+ Popular with Developers
+ Not Opinionated (Flexible); i.e., easy to get started.
+ JavaScript (I already now JavaScript from the client).

- Unknown to Clients
- Not Opinionated (Flexible): i.e., with many different ways of doing things, harder to support others code.

My Opinion on The Future

With the explosive growth in JavaScript development, one can only assume that Node.js / Express will eventually dominate <http://nodejs.org/industry/>.

Example of Building RESTful API on Ruby on Rails

I spent a better part of a day trying to find a good tutorial on this; never found one.  What I ended up doing is going through the main Ruby on Rail tutorial <http://guides.rubyonrails.org/getting_started.html>  (focused on server side HTML generation) and then piecemeal getting to a simplified RESTful API from a myriad of partial tutorials (or written poorly enough that I did not get it).

The key pieces were:

All together the key controller code (simple Create and Read example only) is:

note: In my sample code below, I include all the parenthesis as I find it difficult to understand without it. Apparently, Ruby has a myriad of ways to shortcut code (yuck).

class ArticlesController < ApplicationController

        def index
                articles = Article.all()
                render({json: articles.as_json({only: [:id, :title]})})
        end

        def create
                params().require(:title)
                article = Article.create(params().permit(:title))
                render({json: article.as_json({only: [:id, :title]})})
        end

end

Example of Building RESTful API on Node.js / Express

While the following example is new to me, it walks one through from ground-zero to a RESTful API in a single HTML page.  It does use the express generator (something that I actually never knew about and might use myself despite by avoidance of such things).

http://adrianmejia.com/blog/2014/10/01/creating-a-restful-api-tutorial-with-nodejs-and-mongodb/

Thursday, January 15, 2015

The Brains of the Operation: The Controller

With a handle on the model and the view, we move onto the linchpin of the app; the controller. At the core, the controller code is focused on:
  • Obtaining information from the model and passing it to the view.
  • Obtaining user interactions from the view and passing it onto the model. 
While there are a number of different JavaScript MVC frameworks, I settled on AngularJS <https://angularjs.org/> for the following reasons:
  • It is gaining popularity.
  • It is easy to use.
  • It is owned by Google.
AngularJS provides an excellent tutorial <https://docs.angularjs.org/tutorial> that will get one up to speed sufficient to understand what comes next in this series of posts.  I will refer back to parts of this tutorial through the remaining posts.

UPDATE 1/22/15: Another excellent tutorial that is now being promoted by Angular.js is <http://angular.codeschool.com/>.  It is a video tutorial that is excellent.

From an earlier post, we start with a most basic AngularJS application in JSFIDDLE. Because we want to now use a fairly recent version of AngularJS, we will use the version provided by a Google CDN
<//ajax.googleapis.com/ajax/libs/angularjs/1.3.9/angular.min.js> by adding it to the "External Resources" rather than the older version available in JSFIDDLE.

note: It is important to set the place the JavaScript included in the HTML as "No wrap - in <head>".

In order to make sense of the following starting point, one will need to make it through step 5 in the tutorial: XHRs & Dependency Injection <https://docs.angularjs.org/tutorial/step_05>.

HTML
<div ng-app="myApp">
    <div ng-controller="myCntrl">
       {{test}}
    </div>
</div>

JavaScript
var myApp = angular.module('myApp', []);
myApp.controller('myCntrl', ['$scope', function($scope) {
   $scope.test = 'hello world';
}]);

A Departure to the View

Up until now, we have been working on the data (aka. model) in the SPA and now we are going to focus on the view.  The terms model, view, and controller (later) are general terms to describe kinds of code that follow the Model View Controller (MVC) pattern.
Model–view–controller (MVC) is a software architectural pattern for implementing user interfaces. It divides a given software application into three interconnected parts, so as to separate internal representations of information from the ways that information is presented to or accepted from the user.
Model-view-controller - Wikipedia, the free encyclopedia

There are a myriad of ways to provide for an aesthetically pleasing responsive web design app, but I am only going to focus on one; Bootstrap <http://getbootstrap.com>.  Not going to explain this choice other than, I have barked up many wrong trees before I settled on this solution.

Rather than pointing to a tutorial on Bootstrap (not sure I have found a good one), just going to jump into using it as it is fairly easy.

Screen Layout

With mobile applications on my mind, the first step is create a screen layout that has a header (for things like titles and buttons), a body (for content), and a footer (for more buttons).  The Bootstrap component that we will be using is the panel <http://getbootstrap.com/components/#panels>.

In JSFIDDLE select jQuery 2.1.0 (or greater) in the Frameworks & Extensions and then select Bootstrap 3.2.0.

HTML
<div class="panel panel-default">
  <div class="panel-heading">
    <h3 class="panel-title">Panel title</h3>
  </div>
  <div class="panel-body">
    Panel body
  </div>
  <div class="panel-footer">
    Panel footer
  </div>
</div>

Another thing to consider for mobile devices is to setup the viewport <https://developer.mozilla.org/en-US/docs/Mozilla/Mobile/Viewport_meta_tag>.  In this particular example, I want the app to fill the screen and prevent the user from zooming in.

In order to setup a tag in the header in JSFIDDLE, the trick is to jam it at the end of the CSS as follows:

HTML (in the CSS Section)
</style>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<style>

Result


Writing a Client Using Firebase's Web - JavaScript API

Now that we have built a client to use a RESTful API, we will switch to using Firebase's Web - JavaScript API.

Why? First, Firebases's Web JavaScript API is simple (ok...  so is the client for the RESTful API). Second, most importantly, Firebase's Web JavaScript API is Realtime, i.e., the client is notified of changes in the data.

note: In this particular example, we will not take advantage of the Realtime nature of Web JavaScript API.

In JSFIDDLE, add the following URL to the "External Resources"
<//cdn.firebase.com/js/client/2.0.6/firebase.js>

note: Without the http: or https: the browser will default to the containing page's protocol.

var myDataRef = new Firebase('https://wineapp.firebaseio.com');
var selfRef = myDataRef.child('wineries').once('value', function(snapshot) {
    var wineries = snapshot.val();
    alert(wineries.key1.name);
}, function(error) {
});

Wednesday, January 14, 2015

Writing a Client to Use a RESTful API

Now that one has a RESTful API to test against, one can write a web simple SPA to use it.  To get started writing sample code, create a free JSFIDDLE account <http://jsfiddle.net>.

Pure JavaScript

First, one can write the app without the aid of any JavaScript libraries.
var xmlhttp = new XMLHttpRequest();
var url = "https://wineapp.firebaseio.com/wineries.json";
xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        var wineries = JSON.parse(xmlhttp.responseText);
        alert(wineries.key1.name);
    }
}
xmlhttp.open("GET", url, true);
xmlhttp.send();

With jQuery

One can write the app with the popular jQuery <http://jquery.com> JavaScript library.

note: One has to select jQuery 2.1.0 (or greater) in the Frameworks & Extensions in JSFIDDLE.
$.get( "https://wineapp.firebaseio.com/wineries.json", function(data) {
    alert(data.key1.name);
}).
fail(function() {
});

With AngularJS

While there is a little bit more overhead, which will be useful later, one can use the popular AngularJS <https://angularjs.org> JavaScript library.

note: One has to select AngularJS 1.2.1 (or greater) in the Frameworks & Extensions in JSFIDDLE.

HTML
<div ng-app="myApp">
    <div ng-controller="myCntrl">
    </div>
</div>

JavaScript
var myApp = angular.module('myApp', []);
myApp.controller('myCntrl', ['$http', function($http) {
    $http.get('https://wineapp.firebaseio.com/wineries.json').
        success(function(data, status, headers, config) {
            alert(data.key1.name);
        }).
        error(function(data, status, headers, config) {
        });
}]);


Building the RESTful API

Now that the database and sample data in place, one needs to create a way (API) to get data in and out of the database.

A common mechanism is to create a RESTful API.
Representational State Transfer (REST) has gained widespread acceptance across the Web as a simpler alternative to SOAP and WSDL based Web services. In layman's terms, REST is an architecture style or design pattern used as a set of guidelines for creating web services which allow anything connected to a network (web servers, private intranets, smartphones, fitness bands, banking systems, traffic cameras, televisions etc.) to communicate with one another via a shared common communications protocol known as Hypertext Transfer Protocol (HTTP). 
Representational state transfer- Wikipedia, the free encyclopedia

One of the features of Firebase is that it provides the RESTful API automatically; i.e., no coding or configuring required.  While Firebase provides a complete guide on the API <https://www.firebase.com/docs/rest/guide/>, the examples below will provide the basic concepts.

note: Interestingly enough, in the responsive web design app that we will build we will not be using the RESTful API, but rather the more powerful Web - JavaScript API. The reason that we take a detour to learn using a RESTful API is that there are many other services that one might want to tap into that use a RESTful API, e.g., Google APIs <https://developers.google.com/apis-explorer/>.

Now that one has an idea of what a RESTful API is, one has to have tools to test them.  One particularly easy to use tool is a Google Chrome extension called "Postman - REST Client".  Install Google Chrome and get it.

To get one started here are five things to try in "Postman - REST Client", replacing "wineapp" with the name of your Firebase app. This will exercise the four main data operations in "CRUD", i.e, Create, Read, Update, and Delete.

  • List the (Collection) Wineries:
     
    note: One oddity of the Firebase RESTful API is that is presents lists as an object (key, value) instead of an array of objects as many other RESTful APIs provide.  Another oddity is that one must add the ".json" suffix to the URLs.

  • Retrieve a (Element) Wine:

  • Create a (Element) Winery:


    note: The response will contain the auto-generated key (they list it as the name) of the newly created object.

  • Delete a (Element) Winery:

    note: The ugly string after wineries in the URL is the auto-generated key.

  • Replace a (Element) Wine:


The Database and Sample Data

Going to start building this application by setting up a database and populate it with some sample data.  Not going to worry about authentication, authorization, or validation at this point.

The most familiar database solution is to set up a Structured Query Language (SQL) database, e.g., MySQL. In this example, I will be using a NoSQL, i.e., not SQL, database because it will be quick and easy. In particular, I will be using Firebase.

What is Firebase?
Firebase provides a realtime database and backend as a service. The service provides application developers an API that allows application data to be synchronized across clients and stored on Firebase's cloud.
Firebase - Wikipedia, the free encyclopedia

A particularly important feature of Firebase is that not only will it provide the database but it will also provide the server APIs too.

Getting Started with Firebase

  1. Create a free Firebase <http://www.firebase.com> account and follow the 5 minute tutorial.
  2. Read how to structure data in Firebase <https://www.firebase.com/docs/web/guide/structuring-data.html>. This article is particular important as it is not obvious how to create relationships (e.g., wineries and wines) in a NoSQL database.
Setup Database and Populate It
  1. Create a new empty Firebase app.
  2. Populate the Firebase app with sample data as shown below.  By the way, the trick to creating a nested tree structure using the Firebase "data" screen is to change the browser's URL and load the page, e.g., to create  a winery with the key "key1" use the URL: <https://YOURAPPNAME.firebaseio.com/wineries/key1>.


Setting up the Problem

Through this series of posts I will walk one through the entire thought process in developing a Single Page App (SPA).

The first step is to set up the problem, i.e., what is the app (or series of app) going to do.  Here are the rough requirements.
  • The underlying data model consists of wineries and their list of wines (each wine is uniquely in the list of a single winery, i.e. no wine can be listed under more than one winery).
  • The data store only has wines that are in the list of a winery. 
  • The data store of wineries and wines are to each have required names that are no more than 255 char. long and consist of the characters a-z, A-Z, 0-9, and a space.
  • There needs to be a responsive web design app for unauthenticated users to read the data structure, i.e., can obtain a :
    • list of wineries
    • list of wines
    • winery with the list of its wines
    • wine with its winery
  • There needs to be an easy to use application (can be the same responsive web design app as above) for authenticated and authorized users to write the data structure, i.e., can:
    • create a winery
    • update a winery
    • delete a winery (along with the wines in its list)
    • create a wine (must be associated to a winery)
    • update a winery (must continue to be associated to a winery)
    • delete a wine