A Simple Web Application for Querying Bitcoin Price Information

Written by NickWal

This is the second part of a tutorial demonstrating the ability to re-use JavaScript in many different environments. The first part dealt with the creation of the re-usable service, a simple command line wrapper which called the service as well as a simple test which checks the service is actually working as expected. This second part of the tutorial assumes that you have understood the points raised in the first tutorial.

Project Set-up

You can continue to use the old directory containing the old service, but I recommend you create yourself an new project environment like this:

Create a new directory mkdir crprice or md crprice or whatever your operating system supports and cd to it. Then use the following:

   npm install -g npm-package-bin xo ava gulp-cli
   npm init -y
   xo --init --esnext
   ava --init
   npm install --save angular bluebird request request-promise woofwoof 
   npm install --save-dev ava browser-sync browserify browserify-css fs-extra glob grunt grunt-browserify grunt-contrib-connect grunt-contrib-watch gulp gulp-rename gulp-util vinyl-source-stream xo 

   npm-bin add crprice ./price-tool.js -n

Price Service

The price service is exactly the same as in the previous example. Interestingly although the environment will be different - instead of running in a nodejs server environment, the application will be running in the sandboxed environment of the browser, we can still use exactly the same code, even to access network resources, by using the tool browserify. This not only resolves code that has been required through npm modules, but also provides a wrapper which allows the developer to access services that nodejs would provide natively through the browser instead.

Below the full source code of the service. Just copy and paste this and save with a name price-service.js

   /* jshint node:true */
   /* jshint esversion:6 */
   'use strict';
   const rp = require('request-promise');

   // https://coinmarketcap.com/api/

   module.exports = function () {
       this.getPrice = function (id) {
           const uri = 'https://api.coinmarketcap.com/v1/ticker/' + id + '/';
           const options = {
               url: uri,
               json: true
           };
           return rp.get(options).then(a => {
               return a;
           });
       };
   };

Web Application

For the web application, I have chosen to use AngularJS. It seemed to be to to be the way of using the minimal amount of code to write a wrapper around the service, in addition there is a thriving open source community around AngularJS and consequently is well supported. For the web application itself, we’ll add a couple more files to the project, firstly the index.html page which we’ll put in a subdirectory to help keep the project organized

Below the full source code of the index.html . Just copy and paste this and save with a name index.html in a subdirectory below the project’s root called app.

   <html>
   <head>
     <title></title>
    <script src="scripts/app.js"></script>
    </head>
   <body ng-app="myApp">
   <div ng-controller="MyAppCtrl">
     {{variable}}
     <button ng-click="updateprice()">Update Price</button>
   </div>
   </body>
   </html>

and then we’ll add the JavaScript code, which processes the click, calls the price service, and updates the web page with the price that was returned.

Below the full source code of the index.html . Just copy and paste this and save with a name app.js in a subdirectory below the project’s root called app/js.

   /* jshint node:true */
   /* jshint esversion:6 */

   'use strict';

   var angular = require('angular');

   angular.module('myApp', [])
   .controller('MyAppCtrl', ['$scope', 'priceService', function ($scope, priceService) {
       $scope.variable = '-none-';
       $scope.updateprice = function () {
           console.log('requesting price');
           priceService.getPrice('bitcoin').then(result => {
               var price = result[0];
               $scope.variable = price.symbol + ' ' + price.price_usd + ' USD';
               $scope.$apply(); // make sure async update is visible
           });
       };
   }]).service('priceService', require('../../price-service.js'));

You’ll note of course that the application doesn’t use any style sheets or attractive layouts, to keep the examples clean. Quite naturally there are no limitations here, and you can go on to style our application as you please.

Take note of the very last line of the service, and the simplicity with which the price service we developed is included in the AngularJS app and made available as a service within the app.

Gulp to build the application

You will notice that you cannot just load the index.html page and get the application to run. This is it’s first necessary to run browserify on the application code to convert the code for use in a browser. Running this every time during a build - test - develop cycle would be extremely tedious, so below I have included a helper script, which helps the process along. It runs a web server, which you can use to load the application from, and watches your development directory, when it sees the developer has made a chance to the code, it rebuilds the application, and then automatically triggers the browser to refresh so the new code is visible in the web browser.

Below the full source code of the gulp file. Just copy and paste this and save with a name gulpfile.js in the projects root.

   var path = require('path');
   var gulp = require('gulp');
   var browserSync = require('browser-sync').create();
   var gutil = require('gulp-util');
   var browserify = require('browserify');
   var sourceStream = require('vinyl-source-stream');
   var fse = require('fs-extra');
   var rename = require('gulp-rename');

   // process JS files and return the stream.
   gulp.task('js', function () {
       var bundleStream = browserify()
           .add('app/js/app.js')
           .ignore('cls-bluebird')
           .transform('browserify-css', {
       autoInject: true,
       rootDir: 'www',
       processRelativeUrl: function (relativeUrl) {
           var stripQueryStringAndHashFromPath = function (url) {
               return url.split('?')[0].split('#')[0];
           };
           var replaceAll = function (find, replace, str) {
               var found = find.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
               return str.replace(new RegExp(found, 'g'), replace);
           };
           console.log(relativeUrl);
           var rootDir = path.resolve(process.cwd(), 'www');
           var relativePath = stripQueryStringAndHashFromPath(relativeUrl);
           console.log(relativePath);
           var queryStringAndHash = relativeUrl.substring(relativePath.length);

                   //
                   // Copying files from '../node_modules/bootstrap/' to 'dist/vendor/bootstrap/'
                   //
           var sep = path.sep;
           var prefix = '..' + sep + 'node_modules' + sep;
           if (relativePath.startsWith(prefix)) {
               var vendorPath = 'vendor' + sep + relativePath.substring(prefix.length);
               var source = path.join(rootDir, relativePath);
               var target = path.join(rootDir, vendorPath);

               gutil.log('Copying file from ' + JSON.stringify(source) + ' to ' + JSON.stringify(target));
               fse.copySync(source, target);

                       // Returns a new path string with original query string and hash fragments
               var result = replaceAll('\\', '/', (vendorPath + queryStringAndHash));
               console.log(result);
               return result;
           }

           return relativeUrl;
       }

   })
           .bundle();

       return bundleStream
           .pipe(sourceStream('app.js'))
           .pipe(gulp.dest('www/scripts'));
   });

   gulp.task('copyview', function () {
       return gulp.src('app/index.html')
           .pipe(rename({
       dirname: ''
   }))
           .pipe(gulp.dest('www'));
   });

   // Static server
   gulp.task('dev', ['js', 'copyview'], function () {
       browserSync.init({
           server: {
               baseDir: 'www/'
           }
       });
       gulp.watch('app/**/*.*', ['js-watch']);
   });

   gulp.task('js-watch', ['js', 'copyview'], function () {
       browserSync.reload();
   });
   gulp.task('vs', ['js', 'copyview']);
   gulp.task('default', ['dev']);

to use the script, simply change to the root of the project and type gulp from the command line. If you want to just run the build script without the web server, you can use gulp vs

Running the Application

Once the application is built, you can find the files needed to run the application in the project directory www. These files just need to be copied to a web server which you can browse with your browser.

Continue the tutorial.

The third part of the tutorial available here will take this web application and wrap it in as a mobile application so you can run it on your mobile phone.