Node.js
Workshops


npm init
it creates basic package.json file
name: heroes
name: heroes
version: 1.0.0
name: heroes
version: 1.0.0
description: heroes app
name: heroes
version: 1.0.0
description: heroes app
entry point: index.js
name: heroes
version: 1.0.0
description: heroes app
entry point: index.js
test command: [ENTER]
name: heroes
version: 1.0.0
description: heroes app
entry point: index.js
test command: [ENTER]
git repository: [ENTER]
name: heroes
version: 1.0.0
description: heroes app
entry point: index.js
test command: [ENTER]
git repository: [ENTER]
keywords: [ENTER]
name: heroes
version: 1.0.0
description: heroes app
entry point: index.js
test command: [ENTER]
git repository: [ENTER]
keywords: [ENTER]
license: [ENTER]
Is it ok?: [ENTER]
Lets install our first module!

npm install express --save
{
"name": "node-workshop",
"version": "1.0.0",
"description": "heroes app",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "ssuperczynski <ss@espeo.eu> (espeo.eu)",
"license": "ISC",
"dependencies": {
"express": "^4.13.4"
}
}
Our module has been successfully installed
In package.json we pointed index.js
This is our inital stript
So! Lets crete it
touch index.js
So! Lets crete it
touch index.js

Open index.js
var express = require('express');
var app = express();
Open index.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('viewing single offer');
});
Open index.js
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('viewing single offer');
});
app.listen(3003);
Open browser and go to:
localhost:3003
Open browser and go to:
localhost:3003

var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('viewing single offer');
});
app.listen(3004, function () {
console.log("server started at 3004 port");
});
But we don't know if server works or not
Much better

but hey, I like colors
npm install terminal-colors --save
but hey, I like colors

Oh yea!

Lets refactor index.js
var express = require('express');
var app = express();
app.get('/', getOffer);
function getOffer(req, res) {
res.send('viewing single offer');
}
app.listen(3004, function () {
console.log("server started at 3004 port");
});
index.js
Lets refactor index.js further
function getOffer(req, res) {
res.send('viewing single offer');
}
module.exports = getOffer;
controller/OfferController.js
Lets refactor index.js further
var express = require('express');
var app = express();
var getOffer = require('./controllers/OfferController');
app.get('/', getOffer);
app.listen(3004);
index.js
Nice!
Looks like we have basic node.js structure
Nice!
Looks like we have basic node.js structure

Lets interact with some database

npm install pg pg-hstore --save
But wait, lets do not install Postgres locally
Lets use Docker!
Lets use Docker!

docker-machine start default
eval $(docker-machine env default)
docker-machine start default
eval $(docker-machine env default)
vi docker-compose.yml
docker-machine start default
eval $(docker-machine env default)
version: '2'
services:
postgres:
image: postgres
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: mypassword
vi docker-compose.yml
docker-machine start default
eval $(docker-machine env default)
version: '2'
services:
postgres:
image: postgres
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: mypassword
vi docker-compose.yml
docker-compose up
You now what?
We have postgres!!
For postgress we need some config
So, lets use
sequelize-cli to configure it
Creating out model using

npm install sequelize --save
npm install sequelize-cli --save
node_modules/sequelize-cli/bin/sequelize init
Creating basic schema

node_modules/sequelize-cli/bin/sequelize model:create
--name Offer
--attributes "content:string, title:string"
Update database

node_modules/sequelize-cli/bin/sequelize db:migrate
Lets add more methods to OfferController.js
function getOffer(req, res) { models.Offer.findAll() .then((offers) => { res.send(offers); }) .catch(() => {res.send('Error')}); } module.exports = getOffer;
controller/OfferController.js
But what
models.Offer.findAll()
mean?
But what
models.Offer.findAll()
var sequelize = new Sequelize('postgres://user:pass@example.com:5432/dbname');
var Offer = sequelize.define('Offer', {
title: {type: sequelize.STRING, allowNull: false},
content: {type: sequelize.STRING, allowNull: false}
}, {});
export default Offer;
models/offer.js
mean?
But what
models.Offer.findAll()
mean?
function createOffer(req, res) {
models.Offer.create({
content: req.body.content,
title: req.body.title
})
.then((offer) => { res.send(offer); })
.catch(() => {res.send('Error')});
}
module.exports = getOffer;
controller/OfferController.js
Add new method to index.js
var express = require('express');
var app = express();
var indexAction = require('./controllers/OfferController');
app.get('/', getOffer);
app.post('/', createOffer);
app.listen(3004);
index.js
Add new method to index.js
function createOffer(req, res) {
Offer.create({
content: req.body.content,
title: req.body.title
})
.then((offer) => { res.send(offer); })
.catch((err) => {res.send(err)});
}
controller/OfferController.js
And send something using Postman

We should get

Alright.
But come back to index.js
What does
require('babel-core/register');
mean?
It means,
we can use ES6
instead of ES5
import express from 'express';
import OffersController from './OffersController';
import UsersController from './UsersController';
let router = express.Router();
router.use('/offers', OffersController);
router.use('/users', UsersController);
module.exports = router;
index.js
import express from 'express';
var express = require('express');
export default router;
module.exports = router;
Offer.create({
content: req.body.content,
title: req.body.title
})
.then(offer => res.send(offer))
.catch(err => res.send(err));
Offer.create({
content: req.body.content,
title: req.body.title
})
.then(function(offer) { res.send(offer); })
.catch(function(err) { res.send(err)});
.then((user) => {
return user.get({token: token});
}
.then(user => user.get({token}))
Lets create some authentication


npm install passport passport-http-bearer passport-local bcrypt-nodejs --save
Create User model
node_modules/sequelize-cli/bin/sequelize model:create
--name User
--attributes "email:string, password:string, token:string"
Create Middleware
mkdir middleware
cd middleware
touch passport.js
Register passport strategy
import passport from 'passport';
import local from 'passport-local';
passport.use('local', new local.Strategy(options, callback));
export default passport;
import bcrypt from 'bcrypt-nodejs';
import Promise from 'Bluebird';
instanceMethods: {
comparePassword: function (hash) {
var userPassword = this.password;
var user = this;
return new Promise((resolve, reject) => {
Promise.promisify(bcrypt.compare)(hash, userPassword)
.then((result) => {
resolve([result, user]);
}, (err) => {
reject(err);
});
});
}
},Update User model
hooks: {
beforeCreate: (user, options, cb) => {
Promise.promisify(bcrypt.genSalt)(10)
.then((salt) => {
return Promise.promisify(bcrypt.hash)(user.password, salt, null);
})
.then((hash) => {
user.password = hash;
cb(null, options);
});
}
}
Update User model
import { User } from '../models';
import Promise from 'bluebird';
/// ....
var options = {
usernameField: 'email',
passwordField: 'password'
};
var onLogIn = (email, password, done) => {
User.findOne({where: {email}})
.then(user =>
user ?
user.comparePassword(password) : Promise.reject(new Promise.CancellationError('Wrong email or password'))
)
.spread((isMatch, user) =>
isMatch ?
done(null, user) : Promise.reject(new Promise.CancellationError('Wrong email or password'))
)
.catch(Promise.CancellationError, info => done(null, false, info))
.catch(err => done(err));
};
Add callback to strategy
//controllers/UsersController.js
import express from 'express';
import {User} from '../models';
import passport from 'passport';
let router = express.Router({mergeParams: true});
router.post('/signUp', signUp);
router.post('/logIn', logIn);
export default router;
Create login and register endpoints
function signUp(req, res) {
User.create({
email: req.body.email,
password: req.body.password
}).then(user => res.send({email: user.email }))
.catch(err => res.send(err));
}
function logIn(req, res, next) {
passport.authenticate('local', (err, user, info) => {
if (err) {
return res.send(err);
}
if (!user) {
return res.send(info.toString());
}
return res.send({user});
})(req, res, next);
}
Add child router to main router
// controllers/index.js
import UsersController from './UsersController'; router.use('/users', UsersController);
import passport from './middleware/passport';
app.use(passport.initialize());
Register passport as a middleware:
app.js
Bearer token strategy
Add token helper
// helpers/token.js
import crypto from 'crypto';
import Promise from 'bluebird';
export default function generate() {
return new Promise((resolve, reject) => {
Promise.promisify(crypto.randomBytes)(48).then((buff) => {
resolve(buff.toString('hex'));
}).catch(err => reject(err));
});
}
Modify local-login strategy
User.findOne({where: {email}})
.then((user) => {
return user ? user.comparePassword(password) : Promise.reject(new Promise.CancellationError('Wrong email or password'));
}
)
.spread((isMatch, user) =>
isMatch ?
generateToken()
.then(token => user.update({token}))
.then(() => done(null, user.token))
: Promise.reject(new Promise.CancellationError('Wrong email or password'))
)
.catch(Promise.CancellationError, info => done(null, false, info))
.catch(err => done(err));
Modify UsersController
function logIn(req, res, next) {
passport.authenticate('local', (err, token, info) => {
if (err) {
return res.send(err);
}
if (!token) {
return res.send(info.toString());
}
return res.send({token});
})(req, res, next);
}
// middleware/passport.js
import generateToken from '../helpers/token';
import token from 'passport-http-bearer';
passport.use('token', new token.Strategy((token, done) => {
User.findOne({where: {token}})
.then(user => user ? done(null, user, {scope: 'all'}) : done(null, false))
.catch(err => done(err));
}));
export var tokenAuthentication = passport.authenticate('token', {session: false});
Add token strategy
function singleOffer(req, res) {
Offer.findById(req.params.id)
.then(offer => res.send(offer))
.catch(err => res.send(err));
}
function RespondToOffer(req, res) {
OfferResponse.create({
OfferId: req.body.id,
email: req.body.email,
content: req.body.content
})
.then(response => res.send(response))
.catch(err => res.send(err));
}
router.get('/:id', singleOffer);
router.post('/respond', RespondToOffer);
Add new Offer endpoints
//controllers/OffersController.js
import {tokenAuthentication} from '../middleware/passport';
router.post('/respond', tokenAuthentication, RespondToOffer);
Restrict access to some route
// controllers/UsersController.js
function logOut(req, res) {
User.findOne({ where: {token: req.user.token}})
.then((user) => {
return user.update({token: null});
})
.then(() => {
res.send('Logged out.');
})
.catch(err => res.send(err));
}
router.delete('/logOut', tokenAuthentication, logOut);
Add log out endpoint
Testing
deps
npm install --save-dev supertest expect.js mocha
package.json
"scripts": {
"start": "node bin/index.js",
"build": "gulp",
"test": "npm run test-integration",
"test-integration": "NODE_ENV=test mocha --compilers js:babel-core/register integrationTests "
},
test folder
mkdir integrationTest
cd integrationTest
touch offers.js
integrationTest/offers.js
var request = require('supertest');
var expect = require('expect.js');
var app = require('../bin/index');
var Bluebird = require('bluebird');
var models = require('../models');
describe('offerController', function () {
beforeEach(function () {
return Bluebird.all([
models.Offer.destroy({truncate: true}),
models.OfferResponse.destroy({truncate: true}),
models.User.destroy({truncate: true})
]);
});
it('should have /all endpoint and return with 200', function (done) {
request(app).get('/offers').expect(200, done);
});
});
it('should login and respond to the offer', function (done) {
var token = '';
var newUser = { email: 'julian@espeo.pl', password: 'test' };
models.User.create(newUser).then(() => {
request(app).post('/users/logIn')
.type('form')
.send(newUser)
.expect(200, (err, result) => {
token = result.body.token;
createSomeOffer();
});
});
function createSomeOffer() {
return request(app).post('/offers/')
.type('form')
.send({content: 'you have to code in XML', title: 'some programmer'})
.expect(200, respondToOffer);
}
...
function respondToOffer(err, result) {
if (err) done(err);
request(app).post('/offers/respond')
.set('Authorization', 'Bearer ' + token)
.type('form')
.send({id: result.body.id, email: 'julian@espeo.pl', content: 'i want this job', password: 'test'})
.expect(200, onResponseCreate);
}
function onResponseCreate() {
request(app).get('/offers').expect(200, function (err, result) {
if (err) done(err);
expect(result.body[0].OfferResponses[0].email).to.be('julian@espeo.pl');
expect(result.body[0].OfferResponses[0].content).to.be('i want this job');
done();
});
}
});
Frontend
git checkout newerstuff frontend gulpfile.js
Serving static files
import path from 'path';
app
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use(passport.initialize())
.use('/', router)
.use('/static', express.static('static'));
Serve index.html
// controllers/index.js
router.get('/', (req, res) => res.sendFile(path.join(__dirname + '/../frontend/index.html')));
Add some logging stuff
npm install --save morgan winston
Log requests
import morgan from 'morgan';
import fs from 'fs';
var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'});
app
.use(morgan('dev', {stream: accessLogStream}))
.use(morgan('dev'))
Espeo Node.js Workshops
By Sebastian Superczynski
Espeo Node.js Workshops
- 581