Full stack development

Full stack application development with front-end technologies

January 22, 2017 - 15 minute read -
code web

Introduction

Full stack application development with front-end technologies

Tech stacks

Backend setup and configuration

Initialization

  • Create api-server folder in fullstack-development folder and initialize api-server open terminal and type
$ npm init
  • Open package.json and copy below code in it
{
	"name": "api-server",
	"version": "1.0.0",
	"description": "fullstack application development with front-end technologies",
	"main": "api.js",
	"scripts": {
		"test": "echo \"Error: no test specified\" && exit 1"
	},
	"repository": {
		"type": "git",
		"url": "https://github.com/pradeep1991singh/fullstack-development"
	},
	"keywords": [
		"fullstack",
		"application",
		"development",
		"front-end",
		"only",
		"technologies"
	],
	"author": "pradeep singh",
	"license": "ISC",
	"dependencies": {
		"body-parser": "^1.16.0",
		"express": "^4.14.0",
		"mongoose": "^4.7.7",
		"nodemon": "^1.11.0"
	}
}
  • So, basically above we are defining api-server information related to version, dependencies etc.
  • Main part is dependencies section. We are defining api-server dependencies which is
  • body-parser : for api middleware, handling in-coming request
  • express : for serving REST API
  • mongoose : for mongodb object modeling
  • nodemon : for reloading node server automaticaly

Installation

  • Switch to api-server root and install all dependencies mentioned in package.json open terminal and type
$ npm install

Server structure

fullstack-development/
--- api-server/
------ api.js
------ Todo.model.js
------ package.json

Build api-server

  • We will use mongoDB for persistence back-end data storage.
  • Install mongoDB
  • Run mongoDB, start two terminal, one for mongoDB server and other for mongoDB shell
  • First terminal (run mongoDB server)
$ mongond
  • Second terminal (run mongoDB shell)
$ mongo
  • Create new db mydb and new collection todos. In mongoDB we have collections in place of tables - documentation
  • Create a new file api-server.js and paste below code in that
// api configuration

// express server configuration
var express = require('express');
var api = express();

// body configuration for handling middleware
var bodyParser = require('body-parser');

// mongoose(mongoDB database) configuration
var mongoose = require('mongoose');
var Todo =  require('./Todo.model');

// api port, api will be running on 8080
var port = 8080;

// connect to mongoDB
var db = 'mongodb://localhost/mydb';
mongoose.connect(db);

// bodyParser configuration
api.use(bodyParser.json());
api.use(bodyParser.urlencoded({
  extended: true
}));

// enable cors
api.all('/*', function(req, res, next) {
	console.log('request made for: %s', req.originalUrl);
	res.header('Access-Control-Allow-Origin', '*');
	res.header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
	res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,OPTIONS,DELETE');
	next();
});

// api root
api.get('/', function(req, res) {
  res.send('api running... <br> please use "/api/todos" for getting all todos ');
});

// api listen
api.listen(port, function() {
  console.log('api listening on port ' + port);
});
  • We are serving a mongoDB api server on port 8080

Run api server

  • Switch fullstack-development/api-server folder open terminal and type
$ nodemon
  • Now you should able to see your api server running at http://localhost:8080/
  • Lets add few Rest api end-point for front-end todo applicaton. Paste below code in api-server.js between api root and api listen section.
// get list of todos
api.get('/api/todos', function(req, res) {
  console.log('getting all todos');
  Todo.find({})
    .exec(function(error, todos) {
      if (error) {
        console.log('error getting list');
      } else {
        console.log(todos);
        res.json(todos);
      }
    });
});

// get particular single todo
api.get('/api/todos/:id', function(req, res) {
  Todo.findOne({
    _id: req.params.id
  })
  .exec(function(error, todo) {
    if (error) {
      console.log('error getting');
    } else {
      console.log(todo);
      res.json(todo);
    }
  })
});

// add new todo
api.post('/api/todos', function(req, res) {
  Todo.create(req.body, function(error, todo) {
    if (error) {
      res.send('error saving todo');
    } else {
      console.log(todo);
      res.send(todo);
    }
  });
});

// update todo
api.put('/api/todos/:id', function(req, res) {
  Todo.findOneAndUpdate({
    _id: req.params.id
  },
  {$set: {status: req.body.status}},
  {upsert: true },
  function(error, todo) {
    if (error) {
      console.log('error updating');      
    } else {
      console.log(todo);
      res.sendStatus(204);
    }
  });
});

// remove todo
api.delete('/api/todos/:id', function(req, res) {
  Todo.findOneAndRemove({
    _id: req.params.id
  }, function(error, todo) {
    if (error) {
      console.log('error deleting');      
    } else {
      console.log(todo);
      res.sendStatus(204);
    }
  })
});

  • Now you have your api server listening at 8080 and running with required api end-points. Lets switch to front-end application now.

Front-end setup and configuration

Initialization

  • Create angular-app folder in fullstack-development folder and initialize it with npm init
  • Open package.json and copy below code in it
{
  "name": "todo",
  "version": "1.0.0",
  "description": "todo app written in angular 1",
  "main": "index.html",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/pradeep1991singh/fullstack-development/angular-app.git"
  },
  "keywords": [
    "todo",
    "app",
    "angularjs",
    "1",
    "front-end",
    "mobile",
    "application",
    "full",
    "stack",
    "development"
  ],
  "author": "pradeep singh",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/pradeep1991singh/fullstack-development/angular-app/issues"
  },
  "homepage": "hhttps://github.com/pradeep1991singh/fullstack-development/angular-app#readme",
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.1",
    "gulp-inject": "^4.2.0",
    "gulp-natural-sort": "^0.1.1",
    "gulp-rename": "^1.2.2",
    "gulp-sass": "^3.1.0",
    "gulp-webserver": "^0.9.1",
    "run-sequence": "^1.2.2"
  }
}

Installation

  • Switch to angular-app root and install all devDependencies mentioned in package.json.
  • It will install gulp and required gulp packages for serving angular app open terminal and type
$ npm install

Setup angular-app

  • Basically we will be creating a simple todo application in angular and will consume REST api end-points offered by api-server running at http://localhost:8080/.

App structure

fullstack-development/
--- angular-app/
------ app/
--------- scripts/
------------ controllers/
--------------- todo-list.controller.js
--------------- todo-details.controller.js
------------ services/
--------------- todo.service.js
------------ app.js
--------- views/
------------ todo-list.html
------------ todo-details.html
--------- styles/
------------ styles.css
--------- index.html
--------- bower.json
--------- gulpfile.js
--------- package.json

Build front-end app

  • Create index.html which will be our main entry-point file for serving our application.
<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js" lang="en" ng-app="todo"> <!--<![endif]-->
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Todo app</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

  	<!-- inject:css -->
  	<link rel="stylesheet" href="/bower_components/bootstrap/dist/css/bootstrap.css">
  	<link rel="stylesheet" href="/app/styles/main.css">
  	<!-- endinject -->    

    <!-- inject:js -->
    <script src="/bower_components/angular/angular.js"></script>
    <script src="/bower_components/angular-resource/angular-resource.js"></script>
    <script src="/bower_components/angular-route/angular-route.js"></script>
    <script src="/app/scripts/app.js"></script>
    <script src="/app/scripts/controllers/todo-details.controller.js"></script>
    <script src="/app/scripts/controllers/todo-list.controller.js"></script>
    <script src="/app/scripts/directives/todo-list.directive.js"></script>
    <script src="/app/scripts/services/todo.service.js"></script>
    <!-- endinject -->

  </head>
  <body>
    <!--[if lt IE 7]>
      <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="#">upgrade your browser</a> to improve your experience.</p>
    <![endif]-->

    <div class="col-xs-12" ng-view></div>

  </body>
</html>
  • In index.html we have some bower dependencies lets create bower.json file.
{
  "name": "todo",
  "description": "todo app written in angular 1",
  "main": "index.html",
  "authors": [
    "pradeep singh"
  ],
  "license": "ISC",
  "keywords": [
    "todo",
    "app",
    "angularjs",
    "1",
    "front-end",
    "mobile",
    "application",
    "full",
    "stack",
    "development"
  ],
  "homepage": "",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "angular": "^1.6.1",
    "angular-route": "^1.6.1",
    "bootstrap": "^3.3.7",
    "angular-resource": "^1.6.1"
  }
}
  • Install all bower dependencies open terminal and type
bower install
  • Lets bootstrap our angular-app, create app.js file
angular
  .module('todo', ['ngRoute', 'ngResource'])
  .config(function($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/todo-list.html',
        controller: 'todoListController'
      })
      .when('/todo/:id', {
        templateUrl: 'views/todo-details.html',
        controller: 'todoDetailsController'
      })      
      .otherwise({
        redirectTo: '/'
      })
  });
  • We will be having two views todo-list and todo-details and conrreponding controllers
  • Create todo-list.html in views folder
<h3></h3>

<h4>Create new todo</h4>
<form>
  <div class="form-group">
    <input type="text" class="form-control" id="exampleInputEmail1" placeholder="title" ng-model="title">
  </div>
  <div class="form-group">
    <textarea class="form-control" rows="3" placeholder="description" ng-model="body"></textarea>
  </div>
  <button type="button" class="btn btn-default" ng-click="newTodo()">Create</button>
</form>

<hr/>

<h4>Todo lists</h4>
<div class="list-group">
  <a href="javascript:void(0)" class="list-group-item" ng-repeat="todo in todoList"    ng-click="todoDetails(todo)">
    
    <span class="badge"></span>
  </a>
</div>
  • Create conrreponding todo-list.controller.js controller in scripts/controllers
angular
  .module('todo')
  .controller('todoListController', function($scope, TodoService, $location) {

    $scope.welcomeMessage = "Welcome to todo application";

    // get todo list
    $scope.todoList = [];
    TodoService.list().then(function(response) {
        $scope.todoList = response;
      },
      function(error) {
        console.log(error);
      });

    // add new todo handler
    $scope.newTodo = function() {
      var newTodo = {
        title: $scope.title,
        body: $scope.body,
        status: 'todo'
      };
      $scope.todoList.push(newTodo);

      TodoService.newTodo(newTodo).then(function(response) {
        console.log(response);
      },
      function(error) {
        console.log(error);
      });
    };

    $scope.todoDetails = function(todo) {
      $location.path('/todo/' + todo._id);
    };

  });
  • We have a TodoService in our controller, let create scripts/services/todo.service.js for that.
angular
  .module('todo')
  .service('TodoService', function($q, $resource, $httpParamSerializerJQLike) {

    var baseUrl = 'http://localhost:8080/api';
    var Todo = $resource(baseUrl + '/todos/:id', {id: '@id'}, {
        new: {
            method: 'POST',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            transformRequest: function(obj) {
                var str = [];
                for (var param in obj) {
                  str.push(encodeURIComponent(param) + '=' + encodeURIComponent(obj[param]));
                }
                return str.join('&');
            },                       
        }         
      });

    this.list = function() {
      var defer = $q.defer();
      Todo.query(function(response) {
        defer.resolve(response);
      }, function(error) {
        defer.reject(error);
      });
      return defer.promise;
    };

    this.newTodo = function(newTodo) {
      var defer = $q.defer();
      Todo.new(newTodo, function(response) {
        defer.resolve(response);
      }, function(error) {
        defer.reject(error);
      });
      return defer.promise;
    };

    this.get = function(id) {
      var defer = $q.defer();
      Todo.get({id: id}, function(response) {
        defer.resolve(response);
      }, function(error) {
        defer.reject(error);
      });
      return defer.promise;
    };

    this.delete = function(id) {
      var defer = $q.defer();
      Todo.delete({id: id}, function(response) {
        defer.resolve(response);
      }, function(error) {
        defer.reject(error);
      });
      return defer.promise;
    };            

  });
  • Above we are consuming api create in api-server section and running at http://localhost:8080/.
  • Lets create views/todo-details.html for displaying our todo-details Package
<h3>Todo details page</h3>

<div class="panel panel-default">
  <div class="panel-heading">
    <h3 class="panel-title"></h3>
  </div>
  <div class="panel-body">
    <p></p>
    <p>
      <span>Status: </span>
    </p>

    <button type="button"
      class="btn btn-default"
      ng-if="todo.status!='done'"
      ng-click="done()">
      Done
    </button>      
  </div>
</div>
angular
  .module('todo')
  .controller('todoDetailsController', function($scope, TodoService, $routeParams, $location) {

    // get todo
    TodoService.get($routeParams.id).then(function(response) {        
        $scope.todo = response;
      },
      function(error) {
        console.log(error);
      });

    // delete todo handler
    $scope.done = function() {
      TodoService.delete($routeParams.id).then(function(response) {
        $location.path('/');
      },
      function(error) {
        console.log(error);
      });
    };

  });

Run angular-app

  • We will use gulp for servering and building our angular-app.
  • Create gulpfile.js in angular-app folder
var gulp = require('gulp');
var inject = require('gulp-inject');
var webserver = require('gulp-webserver');
var naturalSort = require("gulp-natural-sort");
var runSequence = require('run-sequence');

var appBaseUrl = 'http://localhost:8000/app/index.html';

gulp.task('inject', function () {
  var target = gulp.src('./app/index.html');
  // It's not necessary to read the files (will speed up things), we're only after their paths:
  var sources = gulp
    .src(['./bower_components/angular/angular.js',
          './bower_components/angular-route/angular-route.js',
          './bower_components/angular-resource/angular-resource.js',
          './app/**/*.js',
          './bower_components/bootstrap/dist/css/bootstrap.css',
          './app/**/*.css'
          ], {
            read: false
          })
    .pipe(naturalSort());

  return target
    .pipe(inject(sources))
    .pipe(gulp.dest('./app'));
});

gulp.task('serve', function() {
  gulp.src('.')
    .pipe(webserver({
      livereload: true,
      directoryListing: true,
      open: appBaseUrl
    }));
});

gulp.task('default', function() {
  // place code for your default task here
  runSequence(
    'inject',
    'serve'
  );  
});
  • Lets run our app using gulp, open new terminal and type (Note our api server should be running)
$ gulp
  • You should be seeing an angular-app running, Create new todo and those will be start listing under Todo lists.

Summary

Fork code at fullstack-development