![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1682517/EPAM_LOGO_Full_Color_RGB.png)
ANDRIY DACENKO
SOFTWARE ENGINEER
September 16, 2015
NODE.JS EXTENDED FEATURES
SOCKETS, MONGO, PASSPORT
JS MENTORING 2015
CONTENT
Static
Dynamic
Immediate
REALTIME
Periodic Polling
Long Polling
Forever Frame
WebSockets
PERIODIC POLLING
CLIENT
SERVER
Time Line
LONG POLLING
CLIENT
SERVER
Time Line
FOREVER FRAME
CLIENT
SERVER
Time Line
WEB SOCKETS
SIMPLE SOCKET
$ npm install socket.io
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io('http://localhost:8080');
socket.on('greet', function (data) {
console.log(data);
socket.emit('message', 'user', 'hello');
});
</script>
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.emit('greet', {
hello: 'world'
});
socket.on('message', function (from, msg) {
console.log('message by ', from, ' saying ', msg);
});
socket.on('disconnect', function () {
socket.emit('user disconnected');
});
});
NAMESPACES
<script>
var chat = io.connect('http://localhost/chat')
, news = io.connect('http://localhost/news');
chat.on('connect', function () {
chat.emit('hi!');
});
news.on('news', function () {
news.emit('woot');
});
</script>
var io = require('socket.io')(8080);
var chat = io
.of('/chat')
.on('connection', function (socket) {
socket.emit('a message', {
that: 'only'
, '/chat': 'will get'
});
chat.emit('a message', {
everyone: 'in'
, '/chat': 'will get'
});
});
var news = io
.of('/news')
.on('connection', function (socket) {
socket.emit('item', { news: 'item' });
});
CALLBACKS
<script>
var socket = io();
socket.on('connect', function () {
socket.emit('call me', 'andrew', function (data) {
console.log(data); // data will be 'holla'
});
});
</script>
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.on('call me', function (name, fn) {
fn('holla');
});
});
BROADCAST & VOLATILE
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
var tweets = setInterval(function () {
getCatsTweets(function (tweet) {
socket.volatile.emit('cats tweet', tweet);
});
}, 100);
socket.on('disconnect', function () {
clearInterval(tweets);
});
});
var io = require('socket.io')(8080);
io.on('connection', function (socket) {
socket.broadcast.emit('new user connected');
});
TASK
-
Create simple server
-
Create input field
-
Create list of messages
Estimates: 10 min
MONGODB
Documents
Collections
Queries
![](http://www.unixstickers.com/image/data/stickers/mongo/mongo.sh.png)
DOCUMENTS
{
"_id" : ObjectId("54c955492b7c8eb21818bd09"),
"address" : {
"street" : "2 Avenue",
"zipcode" : "10075",
"building" : "1480",
"coord" : [ -73.9557413, 40.7720266 ],
},
"grades" : [
{
"date" : ISODate("2015-09-16T00:00:00Z"),
"grade" : "A",
"score" : 11
}
]
}
COLLECTIONS
[{
"_id" : ObjectId("55f89665db8b40a199e357fb"),
"name" : "Andrew"
},
{
"_id" : ObjectId("55f8966bdb8b40a199e357fc"),
"name" : "John"
}]
MONGO SHELL
$ mongo
MongoDB shell version: 2.6.7
connecting to: test
>db
test
>use other
switched to db other
>load('show_databases.js')
{
"databases" : [{
"name" : "admin",
"sizeOnDisk" : 1,
"empty" : true
},{
"name" : "test",
"sizeOnDisk" : 1,
"empty" : true
}],
"totalSize" : 83886080,
"ok" : 1
}
SCRIPTING
// print_all_dos_in_collection.js
cursor = db.collection.find();
while ( cursor.hasNext() ) {
printjson( cursor.next() );
}
# eval method
mongo test --eval \
"printjson(db.collection.find().forEach(printjson))"
# execute for DB
mongo localhost:27017/test show_collection.js
READ
![](http://docs.mongodb.org/manual/_images/crud-query-stages.png)
PROJECTIONS
![](http://docs.mongodb.org/manual/_images/crud-query-w-projection-stages.png)
AGGREGATE
![](http://docs.mongodb.org/manual/_images/aggregation-pipeline.png)
MAP REDUCE
![](http://docs.mongodb.org/manual/_images/map-reduce.png)
BACKUPS
# backup collection
mongodump --db test --collection collection
# backup db
mongodump --host mongodb1.example.net \
--port 37017 \
--username user \
--password pass \
--out /opt/backup/mongodump-2015-09-16
# import
mongoimport --db test \
--collection collecton \
--drop \
--file dataset.json
TASK
-
Import dataset*
-
Find last 5 records
with `Ave` in street
and score >= 10 -
Show only street and
matched grades
Estimates: 20 min
INSERT DOCUMENT
db.inventory.insert(
{
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
},
stock: [
{ size: "S", qty: 25 },
{ size: "M", qty: 50 }
],
category: "clothing"
}
)
INSERT DOCUMENTS
db.inventory.insert(
[{
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
},
{
item: "ABC2",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}]
)
INSERT BULK
var bulk = db.inventory.initializeUnorderedBulkOp();
bulk.insert({
item: "ABC1",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}
);
bulk.insert({
item: "ABC2",
details: {
model: "14Q3",
manufacturer: "XYZ Company"
}
}
);
bulk.execute();
UPDATE DOCUMENT
db.inventory.update(
{ item: "MNO2" },
{
$set: {
category: "apparel",
details: { model: "14Q3", manufacturer: "XYZ Company" }
},
$currentDate: { lastModified: true }
}
)
UPDATE DOCUMENTS
db.inventory.update(
{ category: "clothing" },
{
$set: { category: "apparel" },
$currentDate: { lastModified: true }
},
{ multi: true }
)
UPSERT DOCUMENT
db.inventory.update(
{ item: "TBD1" },
{
item: "TBD1",
details: { "model" : "14Q4", "manufacturer" : "ABC Company" },
stock: [ { "size" : "S", "qty" : 25 } ],
category: "houseware"
},
{ upsert: true }
)
REMOVE DOCUMENTS
// remove all
db.inventory.remove({})
// remove match condition
db.inventory.remove( { type : "food" } )
// remove match condition only one
db.inventory.remove( { type : "food" }, 1 )
MONGOOSE
Schema
Types
Models
Validation
CONNECT
$ npm install mongoose
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var db = mongoose.connection;
db.on('error', function(err) {
console.error('connection error:', err));
});
db.once('open', function (callback) {
console.log('connected');
});
SCHEMA
var animalSchema = new Schema({ name: String, type: String });
MODEL
var Animal = mongoose.model('Animal', animalSchema);
SCHEMA TYPES
-
String
-
Number
-
Date
-
Buffer
-
Boolean
-
Mixed
-
Objectid
-
Array
var schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: { type: Date, default: Date.now },
age: { type: Number, min: 18, max: 65 },
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
array: [],
ofString: [String],
ofNumber: [Number],
ofDates: [Date],
ofBuffer: [Buffer],
ofBoolean: [Boolean],
ofMixed: [Schema.Types.Mixed],
ofObjectId: [Schema.Types.ObjectId],
nested: {
stuff: { type: String, lowercase: true, trim: true }
}
})
INSTANCE METHODS
// assign a function to the "methods" object of our animalSchema
animalSchema.methods.findSimilarTypes = function (cb) {
return this.model('Animal').find({ type: this.type }, cb);
}
var dog = new Animal({ type: 'dog' });
dog.findSimilarTypes(function (err, dogs) {
console.log(dogs);
});
STATICS
// assign a function to the "statics" object of our animalSchema
animalSchema.statics.findByName = function (name, cb) {
return this.find({ name: new RegExp(name, 'i') }, cb);
}
var Animal = mongoose.model('Animal', animalSchema);
Animal.findByName('fido', function (err, animals) {
console.log(animals);
});
VIRTUALS
// define a schema
var personSchema = new Schema({
name: {
first: String,
last: String
}
});
personSchema.virtual('name.full').get(function () {
return this.name.first + ' ' + this.name.last;
});
// compile our model
var Person = mongoose.model('Person', personSchema);
// create a document
var bad = new Person({
name: { first: 'Walter', last: 'White' }
});
console.log('%s is insane', bad.name.full); // Walter White is insane
personSchema.virtual('name.full').set(function (name) {
var split = name.split(' ');
this.name.first = split[0];
this.name.last = split[1];
});
...
mad.name.full = 'Breaking Bad';
console.log(mad.name.first); // Breaking
console.log(mad.name.last); // Bad
bad.name.full = 'Breaking Bad';
MODELS
var schema = new mongoose.Schema({ name: 'string', size: 'string' });
var Tank = mongoose.model('Tank', schema);
var small = new Tank({ size: 'small' });
small.save(function (err) {
if (err) return handleError(err);
// saved!
})
// or
Tank.create({ size: 'small' }, function (err, small) {
if (err) return handleError(err);
// saved!
})
ACTIONS
Tank.find({ size: 'small' })
.where('createdDate')
.gt(oneYearAgo)
.exec(callback);
Tank.remove({ size: 'large' }, function (err) {
if (err) return handleError(err);
// removed!
});
Tank.update({name: 'Tiger'}, {
$set: {size: 'large'}
}, function (err) {
if (err) return handleError(err);
// updated!
});
VALIDATION
All - Required
Numbers - Min & Max
Strings - Enum & Match
Dates - Min & Max
CUSTOM
var toySchema = new Schema({
color: String,
name: String
});
var Toy = mongoose.model('Toy', toySchema);
Toy.schema.path('color').validate(function (value) {
return /blue|green|white|red|orange|periwinkle/i.test(value);
}, 'Invalid color');
var toy = new Toy({ color: 'grease'});
toy.save(function (err) {
console.log(err.errors.color.message)
// prints 'Validator "Invalid color" failed
// for path color with value `grease`'
console.log(err.name) // prints "ValidationError"
console.log(err.message) // prints "Validation failed"
});
PASSPORT
Lighweight
Basic Auth
OAuth
OAuth2
LOCAL
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
app.use(passport.initialize());
passport.serializeUser(function (user, done) {
// console.log('user', user);
done(null, user.username);
});
passport.deserializeUser(function (username, done) {
// console.log('username', username);
User.findOne({
username: username
}, function (err, user) {
return done(err, user);
});
})
app.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login' }
));
OPEN ID
var passport = require('passport')
, OpenIDStrategy = require('passport-openid').Strategy;
passport.use(new OpenIDStrategy({
returnURL: 'http://www.example.com/auth/openid/return',
realm: 'http://www.example.com/'
},
function(identifier, done) {
User.findOrCreate({ openId: identifier }, function(err, user) {
done(err, user);
});
}
));
// Accept the OpenID identifier and redirect the user to their OpenID
// provider for authentication. When complete, the provider will redirect
// the user back to the application at:
// /auth/openid/return
app.post('/auth/openid', passport.authenticate('openid'));
// The OpenID provider has redirected the user back to the application.
// Finish the authentication process by verifying the assertion. If valid,
// the user will be logged in. Otherwise, authentication has failed.
app.get('/auth/openid/return',
passport.authenticate('openid', { successRedirect: '/',
failureRedirect: '/login' }));
OAUTH 1.0
var passport = require('passport')
, OAuthStrategy = require('passport-oauth').OAuthStrategy;
passport.use('provider', new OAuthStrategy({
requestTokenURL: 'https://www.provider.com/oauth/request_token',
accessTokenURL: 'https://www.provider.com/oauth/access_token',
userAuthorizationURL: 'https://www.provider.com/oauth/authorize',
consumerKey: '123-456-789',
consumerSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(token, tokenSecret, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
OAUTH 2.0
var passport = require('passport')
, OAuth2Strategy = require('passport-oauth').OAuth2Strategy;
passport.use('provider', new OAuth2Strategy({
authorizationURL: 'https://www.provider.com/oauth2/authorize',
tokenURL: 'https://www.provider.com/oauth2/token',
clientID: '123-456-789',
clientSecret: 'shhh-its-a-secret'
callbackURL: 'https://www.example.com/auth/provider/callback'
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(..., function(err, user) {
done(err, user);
});
}
));
// only one scope
app.get('/auth/provider',
passport.authenticate('provider', { scope: 'email' })
);
// multiple scopes
app.get('/auth/provider',
passport.authenticate('provider', { scope: ['email', 'sms'] })
);
Q&A Time
ANDRIY DACENKO
SOFTWARE ENGINEER
September 16, 2015
NODE.JS EXTENDED FEATURES
SOCKETS, MONGO, PASSPORT
![](https://s3.amazonaws.com/media-p.slid.es/uploads/66414/images/1682540/Global.png)
Node.js extended features
By Andrew Dacenko
Node.js extended features
JS Mentoring program Kyiv.2015-06 Module 5: Server programming using Node.js Lecture15: Node.js extended features
- 1,245