2. History
• 2009 Created by Ryan Dahl, Joyent
• 2011 NPM, node package manager, was introducted by Isaac
Schlueter
• 2014 Timothy J Fontaine introduced as new project lead
• 2014 npmjs.org currently holds 106 287 packages, on npmjs.org
3. NodeJs
• Is Javascript on the backend
• Is an open source, cross-platform runtime environment for server-
side and networking applications
• Has an event-driven architecture
• Based on google v8 javascript engine
• Is awesome !
5. Open file – non blocking
fs.readFile('file.csv', 'utf-8', function (err, data) {
if (!err) {
console.log('file content' + data);
} else {
console.log('error while opening file' + err);
}
}); callback
Second arg should be result
var fs = require('fs');
First arg should be error
Open file
6. Open file – blocking AVOID
var data;
try {
data = fs.readFileSync('foo.bar');
} catch (e) {
// handle error
}
var fs = require('fs');
blocking
7. Module (assembly)
( 1.1 ) Can refer to a file or a directory
( 2 ) Can be global or local in node_modules
Directory require(’moduleName’)
( 1 ) Can be in your project
// package.json
{ ”start” : ”app.js” }
var myModule = require(’./myModule’);
myModule.method();
( 1.1.1 ) Points to directory
math/index.js
var myModule = require(’myModule’)
Points default to index.js
( 1.1.2 )Points to a file
Default can be changed in package.json
project/node_modules/moduleName
var math = require(’./math’)
8. Example module ( is cached )
var fs = require('fs')
path = require('path')
modulePath = __dirname,
logPath = 'log',
logFile = 'logger.txt';
module.exports.log = function (message){
fs.writeFile( path.join(modulePath, logPath) + logFile,
message,
'utf-8',
function (err, data) {
if (err)
console.log("there was an error " + err);
else
console.log('This was written to file ' + data);
});
}
function privateFunction(){}; var privateVar = ”test”;
module.exports, public property / function
Private area, not seen outside module
10. Package.json
{
"name": "NodejsModules",
"version": "0.0.0",
"description": "NodejsModules",
"main": "app.js",
"author": {
"name": "cno",
"email": ""
},
"devDependencies": {
"express": "",
"gulp": "^3.8.10",
"gulp-mocha": "^1.1.1",
"gulp-nodemon": "^1.0.4",
"gulp-util": "^3.0.1",
"should": ""
}
dependencies : {
”express” : ””,
devDependencies, dependecies that is
needed to work on the project like
preprocessors, testing frameworks,
minification libs etc
npm install <package> --save-dev
dependencies, dependecies that is needed
for your app to run
npm install --save
~ = minor version
~1.2.3 will match all 1.2.x versions but will miss 1.3.0
^= major version
^1.2.3 will match any 1.x.x release including 1.3.0, but will
hold off on 2.0.0
11. API development
• Web framework
• Routing, REST
• Testing, Unit + Integration
• Error recovery, Authentication
• Backend MySql, Mongo, OR- mapper?
• Task runners for restart server +
continuous integration
EXPRESS
EXPRESS
JASMINE + FRISBY
MIDDLEWARES : PASSPORT
MONGO = >MONGOOSE OR
MYSQL/POSTGRES => SEQUELIZE
GRUNT OR GULP, pick one !
12. API - Minimum implementation
var express = require('express');
Var user = require(’./routes/user’)
var app = express();
// all environments
app.set('port', process.env.PORT || 3000);
app.set('env', 'development');
app.use(authMiddleware);
// development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.get('/', routes.index);
app.get('/users', user.list);
app.get('/users/:id', user.getone);
app.use(app.router);
http.createServer(app).listen(app.get('port'), function () {
console.log('Express server listening on port ' +
app.get('port'));
});
Init express
Define routes
Start web server
Add middlewares
13. Routes - CRUD
app.get('/products', function (req,res) {
res.send(musicRepository.get());
});
app.get('/products/:id', function (req, res) {
res.send(musicRepository.get({
id : req.params.id }));
});
app.post('/products', function (req,res) {
musicRepository.create({
name : req.body.name,
price : req.body.price
});
});
app.put('/products', function (req, res) {
musicRepository.create({
id : req.body.id,
name : req.body.name,
price : req.body.price
});
});
POST/PUT requires bodyParser
middleware !!
request.body
request.params
14. Middleware
app.get('/routeThatWillIgnoreAuthentication', function (req, res) {
// do some damage
});
app.use(crappySecurityMiddleware);
function crappySecurityMiddleware(req, res, done){
if (request.headers.token && request.headers.token == "admin") {
done();
} else {
res.send('Not authenticated, please login');
}
// done is NEVER called, request dies here !
}
app.get('/willBeAuthenticated', function (req, res) {
// safer call
});
The middleware
Route NOT using middleware
1
2
1a
Call done unless you
intend to stop execution
15. Demo – Simple api with angular client
• LectureNode + LectureNodeClient
• Do POST call from client and show VS NodeJs Tools, debug
• Also show NPM management, install / update, see how it updates
when things are added to package json
16. Demo Gulp / Nodemon / Frisby
• NodeJsModules
• We want to achieve
• Start with ”gulp develop”
• Node server that relaunches on code change, automatically
• Tests to run often
• End point testing, when needed ”jasmine-node test/api”
17. Unit Tests Mocha, async + sync
describe('call to long running function', function () {
it('should produce data', function (done) {
var req = {
send : function (data) {
expect(data.length).toBe(3);
done();
}
};
var _route.getPerson(req,res);
})
})
done() Signals to test framework that test is done
Request object calls send() when async call is done
18. Mocking
What if you have a method that calls IO/ Database, how to test?
function aMethod(someParam){
if (someParam) {
var content = fs.openFileSync(’file.txt’,’utf-8’);
console.log(content);
} else {
// do something
}
}
Hard to test right?
Use mockery
var mockery = require(‘mockery‘);
var fsMock = { openFileSync : function(){ console.log(“mocked”); } }
mockery.registerMock(‘fs’,fsMock);
Or if its more complicated
mockery.registerSubstitute(‘fs’,’fs-mockedModule’); Replace one module for another
19. Tests - frisby
frisby.create('GET JSON data from /products/1')
.get('http://localhost:8000/products/1')
.expectStatus(200)
.afterJSON(function (body) {
expect(body.id).toMatch(2)
})
.toss();
frisby.create('GET JSON data from /products')
.get('http://localhost:8000/products')
.expectStatus(200)
.afterJSON(function (body) {
expect(body.length).toMatch(2);
})
.toss();
afterJson, investigates the data
coming back from api call
20. Callback hell , the problem
The problem
You want to do several tasks when creating a user
1) Validate user does not exist before
2) Validate input data is valid
3) Create person in db
4) Write to event log ’person created’
5) Respond with created user
Some operations are async and therefore we get a
callback tree
repo
.getUser(req.body.name)
.success(function (data) {
if (data == null) {
repo
.createPerson(req.body)
.success(function (person) {
repo
.createEvent(’person created’)
.success(function () {
res.send(data);
});
});
}
}); Sending data to user
21. Introducing the event emitter
emitter.on(’eventName', callback);
var events = require(’events’);
var EventEmitter = = new events.EventEmitter();
function doStuff(){
emitter.emit(’eventName’,<optional data>);
}
Trigger the event
setup event and act accordingly
function callback(<optional event data>){
}
22. Callback hell – a solution
function CreatePerson(){
var me = this;
function _validateUserDidNotExistBefore(){
me.emit('ev_userDidNotExistBefore');
}
this.on('ev_userDidNotExistBefore', _validateInputData);
this.on('ev_inputValidated', _createPerson);
this.on('ev_personCreated', _createLogEventEntry);
this.on('ev_error', function () {
res.send('error');
})
this.create = function (){
_validateUserDidNotExistBefore();
}
}
util.inherits(CreatePerson, EventEmitter);
Public function
Chain of events,” if x then y”
Event trigger
CreatePerson inherits from EventEmitter