Prerequisites
GitHub account, Heroku account, Git installed, MongoDB installed (shell runs) , latest stable Node.js installed (with NPM) so the shell runs
Introduction to MEAN Stack
Basic Concepts :
-
JavaScript Fundamentals
- intro : http://www.w3schools.com/js/
- javascript object : http://javascriptissexy.com/javascript-objects-in-detail/
- javascript scope : http://javascriptissexy.com/javascript-variable-scope-and-hoisting-explained/
- javascript closures : http://javascriptissexy.com/understand-javascript-closures-with-ease/
- Best Practices :
- always use closures
- prefer functional inheritance over pseudo-classical inheritance
- prefer === (strict type checking) vs == (auto-boxing)
- adopt object-oriented patterns
- more …. http://www.w3schools.com/js/js_best_practices.asp
-
NodeJs Fundamentals
- intro : http://courseware.codeschool.com/node_slides.pdf
- simple step by step guide : http://node.codeschool.com/levels/1/challenges/1
- understand the concepts of event loop and callback
- nodejs spawns a process when a script is executed and provides access to internal process
- understand the concepts of variable scope (only process and global are in global scope)
- Always perform the logic in callbacks and chain the callbacks
- official tutorials to understand the main modules : http://nodejs.org/documentation/tutorials/
- another beginner guide : http://nodeguide.com/beginner.html
- simple step by step guide : http://node.codeschool.com/levels/1/challenges/1
- hands on coding and graduate with nodejs
- during the training session we started coding through an interactive workbench
- https://www.npmjs.org/package/learnyounode
- sudo npm install learnyounode -g
- learnyounode help
- vi program.js
- learnyounode verify program.js
- The idea is to try out all the exercises :
- For example a simple nodejs code
- intro : http://courseware.codeschool.com/node_slides.pdf
var args = process.argv; var result = 0; for(var i =0; i < args.length; i++){ if(i > 1) { result = result + Number(args[] ); } } console.log(result);
A reference book with examples – Rapid Prototype with JS : https://gumroad.com/l/rpjs/A7BB7934
-
-
Getting feet wet with Code :
- One can write the code on his own and then verify with the working version from the repo https://github.com/azat-co/rpjs
- presentations : http://slides.com/azat/npa#/0/1Express Book Code repo : https://github.com/azat-co/proexpressjs/tree/140d802c752def44856d4befcf5c1baa14efc257
- Once we are confident with working on nodejs in local box, we can start deployment on Heroku
- the folder should contain – package.json , Procfile , <your_app>.js
-
Procfile content : web: node serverTest.js
- heroku create
- git add .
- git commit -am ‘cloud deployment’
- git push heroku master
-
- you can also deploy on OneOps Nodejs Platform using CLI
- the folder should contain – package.json , Procfile , <your_app>.js
- The most exciting part is CRUD in MongoDB from Nodejs
- MongoDB Basics : http://docs.mongodb.org/manual/tutorial/getting-started/
- Complete Code Nodejs + Express + MongoDB : https://github.com/azat-co/rpjs/blob/master/mongo/web.js
-
-
MEAN Fundamentals
1. http://scotch.io/tutorials/javascript/creating-a-single-page-todo-app-with-node-and-angular
2. http://scotch.io/bar-talk/setting-up-a-mean-stack-single-page-application
3. https://www.codeschool.com/courses/real-time-web-with-node-js
Introduction to Angular JS
- Learn basics : https://egghead.io/ , http://learn-angular.org/
- Main Focus : Understanding Reactive Data Binding , Auto-bootstraping using ng-app , Custom and built-in Directives,
Scope, Dependency Injection through Annotations, Controllers, Service, Promise, RestAngular - Angular code available in https://github.com/kaniska/angularjs-sample
http://js/github_controller.js
Using Angular Service :
https://github.com/kaniska/angularjs-sample/blob/master/js/github_controller.js
.service(‘GithubService’, function($http) {this.search = function(q) {return $http({method: ‘GET’,params: {q: q}}).then(function(resp) {return resp.data;});};}).controller(‘GithubController’,function($scope, GithubService) {$scope.search = function() {// Run the search on github// and place results in `results`GithubService.search($scope.q).then(function(data) {$scope.total_result_count = data.total_count;$scope.results = data.items;});}})
Advanced Concepts
npm install expressworks
Rest API Code : https://github.com/azat-co/rest-api-express
comparison between Hapi, Sails, Express :
http://runastartup.com/express-js-vs-sails-js-comparison/
Hapi code : github.com/azat-co/practicalnode/blob/master/ch8/rest-hapi/
Validation using JOI modules , Request / Response Event handlers
Built-in Authn , API Configurations
Sails code :
uses Waterline ORM , Generates scaffolds , Bundles static files with Grunt
Single Page Application
https://github.com/azat-co/rpjs/tree/master/board
Backbone : Router Model Collection View
Flow Control
- Return : http://www.w3schools.com/js/
-
Callbacks :
CALLBACKS – PART 1
if (error) {
callback(error);
} else {
db.find({…}, function() { … }) }
if (error) return callback(error);
db.find({…}, function(){ … })
CALLBACKS – PART 2
exports.getUsers = function(req, res, next) {
if (req.session.auth && req.session.userId) {
req.db.User.find({}, safeFields, function(err, list) {
if (err) return next(err); res.status(200).json(list); });
} else { return next(‘User is not recognized.’) } }
-
Async : ( waterfall, compose, seq, applyEachSerial, queue )
async.waterfall([
function(callback){
callback(null, ‘one’, ‘two’);
},
function(arg1, arg2, callback) { // arg1 now equals ‘one’ and arg2 now equals ‘two’
callback(null, ‘three’); },
function(arg1, callback) { // arg1 now equals ‘three’
callback(null, ‘done’); }
], function (err, result) { // result now equals ‘done’
});
-
Promises : (manage callback hell)
q (https://github.com/kriskowal/q)
promised-io (https://github.com/kriszyp/promised-io)
function promisify(nodeAsyncFn, context) {
return function() {
var defer = q.defer() , args = Array.prototype.slice.call(arguments);
args.push(function(err, val) { if (err !== null) {
return defer.reject(err);
}
return defer.resolve(val);
});
nodeAsyncFn.apply(context || {}, args);
return defer.promise;
var readFile = promisify(fs.readFile);
readFile(‘test.txt’).then(function(data) { console.log(data); });
Restful Sessions
Redis + Express
npm install connect-redis express-session
var session = require(‘express-session’),
RedisStore = require(‘connect-redis’)(session);
app.use(session({
store: new RedisStore(options), secret: ‘keyboard cat” }));
Redis Advanced
var SessionStore = require(‘connect-redis’);
var session = require(‘express-session’);
app.use(session({
key: ’92A7-9AC’, secret: ’33D203B7-443B’, store: new SessionStore({ cookie: { domain: ‘.webapplog.com’ }, db: 1, // Redis DB host: ‘webapplog.com’ }));
Clustering the app in a box
var cluster = require(‘cluster’);
var http = require(‘http’);
var numCPUs = require(‘os’).cpus().length;
var express = require(‘express’);
if (cluster.isMaster) {
console.log (‘ Fork %s worker(s) from master’, numCPUs)
for (var i = 0; i < numCPUs; i++) { cluster.fork(); };
cluster.on(‘online’, function(worker) {
console.log (‘worker is running on %s pid’, worker.process.pid) });
cluster.on(‘exit’, function(worker, code, signal) {
console.log(‘worker with %s is closed’, worker.process.pid); });
} else if (cluster.isWorker) {
var port = 3000;
console.log(‘worker (%s) is now listening to http://localhost:%s‘, cluster.worker.process.pid, port);
var app = express();
app.get(‘*’, function(req, res) {
res.send(200, ‘cluser ‘ + cluster.worker.process.pid + ‘ responded \n’); })
app.listen(port);
}
Error Handling
server.on('error', function (err) { console.error(err); ... })process.on('uncaughtException', function (err) { console.error('uncaughtException: ', err.message); console.error(err.stack); process.exit(1); }); process.addListener('uncaughtException', function (err) { console.error('uncaughtException: ', err.message); console.error(err.stack); process.exit(1); });
Error Notification
https://gist.github.com/azat-co/67509d5c8e38d2f5f4dd
var sendHipChatMessage = function(message, callback) {
var fromhost = server.set(‘hostname’).replace(‘-‘,”).substr(0, 15); //truncate the string
try {
message = JSON.stringify(message);
} catch(e) {}
var data = {
‘format’: ‘json’,
auth_token: server.config.keys.hipchat.servers,
room_id: server.config.keys.hipchat.serversRoomId,
from: fromhost,
message: ‘v’+ server.set(‘version’)+ ‘\nmessage: ‘+ message
};
request({
url:‘http://api.hipchat.com/v1/rooms/message‘,
method:‘POST’,
qs: data}, function (e, r, body) {
if (e) console.error(e);
if (callback) return callback();
});
};
server.notify = {};
server.notify.error = function(e) {
var message = e.stack || e.message || e.name || e;
sendHipChatMessage(message);
console.error(message);
server.sendgrid.email({
to: ‘error@webapplog.com‘,
from: server.set(‘hostname’) + ‘@webapplog.com‘,
subject: ‘Webapp ‘+ server.set(‘version’)+ ‘ error: “‘+ e.name+ ‘”‘,
category: ‘webapp-error’,
text: e.stack || e.message
}, exit);
return;
}
Handle Async Error
This is not an error-prone solution
try { // throw new Error(‘Fail!’);
setTimeout(function () {
throw new Error(“Fail!”); },
Math.round(Math.random()*100));
} catch (e) {
console.log(‘Custom Error: ‘ + e.message);
}
Elegant solution using Domain
Basic Domain Example :
var domain = require(‘domain’).create();
domain.on(‘error’, function(error) {
console.log(error); });
domain.run(function(){ throw new Error(‘Failed!’); });
Resolve Async Error Handling using Domain
var domain = require(‘domain’);
var d = domain.create();
d.on(‘error’, function(e) { console.log(‘Custom Error: ‘ + e); });
d.run(function() {
setTimeout(function () {
throw new Error(‘Failed!’); }, Math.round(Math.random()*100)); });
Domain + Express
Debugging in Nodejs Environment
var net = require('net'),
options = {name: 'azat'};
net.createServer(function(socket) {
repl.start(options.name + "> ", socket).context.app = app;
}).listen("/tmp/debug-app-" + options.name);
$ ssh ... $ telnet /tmp/repl-app-azat >
Forever + Upstart
$ npm install -g forever
$ forever start -l forever.log -o output.log -e error.log server.js
$ forever stop server.js
$ forever list
$ forever –help
author “Kaniska”
description “practicalnode”
setuid “nodeuser”
start on startup
stop on shutdown
respawn
env NODE_ENV=production
exec forever start /var/practicalnode/webapp.js >> /var/log/yourprogram.sys.log 2>&1
Serve Static Content using Nginx
sudo vim /etc/nginx/conf.d/virtual.conf
server { location / {
proxy_pass http://localhost:3000;
}
location /static/ {
root /var/www/webapplog/public;
}
}
$ sudo service nginx start
MongoDB Drivers
For schema less CRUD use http://mongodb.github.io/node-mongodb-native/contents.html
Use mongoose for ORM . Definitely its slow for complex queries.
NodeJs Frameworks Comparison :
Log Management
Papartrailapp -> aggregate logs from each server in cluster
Bunyan offers log rotation.
Test
superagent (rest test)
casperjs (browser test)
Module Dependency Management
shrinkwrap
Further references :
- http://strongloop.com/strongblog/node-js-is-faster-than-java/
- http://www.nodewiz.biz/your-doing-node-js-wrong-avoid-synchronous-code/
- Parallel Calls : https://github.com/caolan/async , http://book.mixu.net/node/ch7.html
- Managing Callbacks : http://strongloop.com/strongblog/promises-in-node-js-with-q-an-alternative-to-callbacks/
- Walmart Nodejs Web Fwk (Hapi) : https://github.com/spumko/hapi/blob/master/docs/Reference.md, https://github.com/spumko/hapi/tree/master/examples , http://blog.modulus.io/nodejs-and-hapi-create-rest-api
- Backbone.js : http://addyosmani.github.io/backbone-fundamentals/