it creates basic package.json file
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
var express = require('express');
var app = express();
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('viewing single offer');
});
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.send('viewing single offer');
});
app.listen(3003);
localhost:3003
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");
});
npm install terminal-colors --save
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
function getOffer(req, res) {
res.send('viewing single offer');
}
module.exports = getOffer;
controller/OfferController.js
var express = require('express');
var app = express();
var getOffer = require('./controllers/OfferController');
app.get('/', getOffer);
app.listen(3004);
index.js
Looks like we have basic node.js structure
Looks like we have basic node.js structure
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
npm install sequelize --save
npm install sequelize-cli --save
node_modules/sequelize-cli/bin/sequelize init
node_modules/sequelize-cli/bin/sequelize model:create
--name Offer
--attributes "content:string, title:string"
node_modules/sequelize-cli/bin/sequelize db:migrate
function getOffer(req, res) { models.Offer.findAll() .then((offers) => { res.send(offers); }) .catch(() => {res.send('Error')}); } module.exports = getOffer;
controller/OfferController.js
models.Offer.findAll()
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
models.Offer.findAll()
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
var express = require('express');
var app = express();
var indexAction = require('./controllers/OfferController');
app.get('/', getOffer);
app.post('/', createOffer);
app.listen(3004);
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
require('babel-core/register');
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}))
npm install passport passport-http-bearer passport-local bcrypt-nodejs --save
node_modules/sequelize-cli/bin/sequelize model:create
--name User
--attributes "email:string, password:string, token:string"
mkdir middleware
cd middleware
touch passport.js
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);
}
// 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
// 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
"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 "
},
mkdir integrationTest
cd integrationTest
touch 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();
});
}
});
git checkout newerstuff frontend gulpfile.js
import path from 'path';
app
.use(bodyParser.urlencoded({extended: true}))
.use(bodyParser.json())
.use(passport.initialize())
.use('/', router)
.use('/static', express.static('static'));
// controllers/index.js
router.get('/', (req, res) => res.sendFile(path.join(__dirname + '/../frontend/index.html')));
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'))