Animée par Charles Jacquin

Sommaire :

  • Introduction
  • Node.js core
  • Serveur web
  • Communication avec une base de donnée
  • Communication en temps réel avec socket.io
  • Test et deploiement

INTRODUCTION

Historique

  • Créé par Ryan Dahl en 2009
  • Sponsorisé par Joyent (cloud)
  • Construit a partir de la VM de Chromium v8
  • Inspiré par Ruby

Installation

Rendez vous sur le site officiel et suivez les instructions.

Pour Ubuntu il existe un PPA.

Node Package Manager

Node.js est fournis avec npm, un gestionnaire de paquet.

  • équivalent de gem pour Ruby
  • équivalent de maven pour Java
npm install <nom_du_module> #installation dans le repertoire courant

npm install <nom_du_module> -g  #installation globale

Package.json

Le fichier package.json se trouve à la racine du projet.

Permet de définir de quels modules depend notre application

npm init #initialisation du fichier package.json

{
  "name": "myApp",
  "version": "0.0.1",
  "description": "A wonderfull app",
  "main": "index.js",
  "scripts": {
    "prestart": "npm update",
    "start": "node server.js",
    "test": "mocha"
  },
  "author": "Me <me@myCorp.org> (https://github.com/me)",
  "license": "MIT",
  "dependencies": {
    "express": "*",
    "lodash": "*",
    "mongoose": "*"
  },
  "devDependencies": {
    "should": "*",
    "mocha": "*",
    "supertest": "*",
  }
}

Npm en detail

  • plus de 135 000 packages
  • plus d'un milliard de téléchargement par mois

Npm en chiffre :

https://www.npmjs.com/  est un moteur de recherche.

npm search <nom_du_module>
#retourne la liste des module correspondant à la recherche

npm install <nom_du_module> --save
#installation locale et mise à jour des dependencies

npm install <nom_du_module> --save--dev 
#installation locale et mise à jour des devDependencies

Du JavaScript éxécuté sur le serveur

Deux choses :

Node utilise v8 le moteur JavaScript de chromium.

Non bloquant.

V8

Moteur Javascript de Chrome

Compilation "Just In Time"

Non bloquant I/O

Le JavaScript est construit autour

de la notion d' événement.

Le roi et ses servants

La même chose en Java

Un peu de code

function doSyncCall(data){
    data++;
    return data;
}

var data = 1;
console.log(doSyncCall(data));
function doAsyncCall(data,callback){
    data++;
    callback(data);
}

var data = 1;
doAsyncCall(data, newData => {
    console.log(newData);
});

Traitement synchrone

Traitement asynchrone

Gestion des erreurs

Gestion des erreurs

Pour un traitement synchrone

function doSyncCall(data){
    if(typeof data !== 'number'){
        throw new Error('1st param must be a number');
    }else{
        return data++;
    }
}
var data = 1;
try{
    console.log(doSyncCall(data));
}catch(err){
    console.log(err);
}

Gestion des erreurs

Les try catch ne servent à rien pour les traitements asynchrone.

function doAsyncCall(data,callback){
    if(typeof data !== 'number'){
        callback(new Error('1st param must be a number'));
    }else{
        data++;
        callback(null,data);
    }
}
var data = 1;
doAsyncCall(data, (err, newData) => {
    if(err){
        console.log(err.message);        
    }else{
        console.log(newData);    
    }
});

Le premier paramètre du callback sera toujours l'objet d'erreur

Le deuxième contient la valeur de retour de la fonction

Pyramid of doom

function doAsyncCall(data,callback){
    data++;
    callback(data);
}

var data = 1;
doAsyncCall(data,function(firstData){
    doAsyncCall(firstdata,function(secondData){
        doAsyncCall(secondData,function(thirdData){
            doAsyncCall(thirdData,function(fourthData){
                doAsyncCall(fourthData,function(fifthData){
                    doAsyncCall(fifthData,function(finalData){
                        //do something with finalData
                    });
                });
            });
        });
    });
});

DOOM

Les promises

function doAsyncCall(data){
    return new Promise((resolve,reject) => {
        if(typeof data !== 'number'){
            reject(new Error('error !!!'));
        }else{
            resolve(data++);
        }
    })
}
var data = 1;
doAsyncCall(data)
    .then((newData) => {
        console.log(newData); //affiche 2
    })
    .catch(function(err){
        console.error(err);
    });

Permettent de limiter la taille de la pyramide.

Avant la version 0.12, il fallait utiliser une librairie tierce comme q ou bluebird.

Les promises sont natives dans ECMA6 et sont implémentées dans la version 0.12 de Node.js

Chainer des promises

function doAsyncCall(data){
  return new Promise((resolve,reject) => {
    setTimeout(() => {
      if(typeof data !== 'number'){
        reject(new Error('error !!!'));
      }else{
        resolve(data++);
      }
    }, 30)
  })
}
var data = 1;
doAsyncCall(data)
  .then(newData => {
    return doAsyncCall(newData);
  })
  .then(newData => doAsyncCall(newData)
  .then(finalData => {
    console.log(finalData) //affiche 3
  })
  .catch(function(err){
    console.error(err);
  });

En parallèle

function doAsyncCall(data){
  return new Promise((resolve,reject) => {
    setTimeout(() => {
      if(typeof data !== 'number'){
        reject(new Error('error !!!'));
      }else{
        resolve(data++);
      }
    }, 30)
  })
}
const data = 1;
const anOtherData = 2;

Promise.all([
  doAsyncCall(data),
  doAsyncCall(anOtherData),
  anOtherPromise
]).then(function(data){
//data est un tableaux avec 3 entrées
}).catch(function(err){

});

Node.js Core

Les objets globaux

Global

Dans le browser                         est declaré dans le scope global

var foo = 'bar';

Dans node, le scope de foo serait le scope du module courant.

Déclare une variable globale.

global.foo ='bar';

Les objets globaux

console

Le même objet que dans le browser

__filename

Le fichier courant

__dirname

Le répertoire courant

Les objets globaux

setTimeout() et clearTimeout()

setInterval() et clearInterval()

Fonctionne comme dans le browser

Les modules de base

Node.js contient de base un certain nombre de paquet.

  • fs
  • path
  • http, https
  • ...

Importer un module

Le mot clef "require" permet de récupérer un module.

var fs = require('fs');

fs.readFile('package.json', (err,data) => {
    if(err){
        console.log('error',err);
    }else{
        console.log('success',data);
    }
});
var pkg = require('./package');

console.log(pkg);

ou un fichier

require.main renvoie le fichier principal de l'application.

Gestion du cache

Les modules sont mis en cache.

Pour recharger le module il faut manipuler require.cache.

require('fs'); //load module

require('fs'); //from cache

delete require.cache.fs

require('fs'); //load module

Exporter un module

L'objet module, nous permet d'exporter un objet.

module.exports = {
    findAll(){

    },
    create(){

    },
    remove(){

    },
    update(){

    }
}

dao.js

var dao = require('./dao');


dao.findAll();

index.js

L'objet ainsi exporté est utilisable dans les autres fichiers de notre application.

Manipuler des fichiers

Le module fs, permet la manipulation de fichier.

Simples "wrapper" autour des fonctions standard UNIX.

fs : méthodes asynchrones

  • fs.mkdir()

  • fs.readFile()

  • fs.rename()

  • fs.unlink()

  • ...

const fs = require('fs');

fs.mkdir('./foo', err => {
    
})

fs.rename('./foo/bar.txt','./foo/toto.txt', err => {
    
})

fs.writefile('./foo/bar.txt', data, err => {
    
})

fs.readfile('./foo/bar.txt', { encoding: 'utf8' }, err => {
    
})

fs : méthodes synchrones

  • fs.mkdirSync()

  • fs.readFileSync()

  • fs.renameSync()

  • fs.unlinkSync()

  • ...

var fs = require('fs'),
    fileContent;

try{

    fileContent = fs.readfileSync('./foo/bar.txt')

}catch(err){

    console.log(err);

}

Toutes ces méthodes existent aussi en version synchrone.

Pour des raisons de performances toujours privilégier les méthodes asynchrones.

fs-promise

Les mêmes méthodes que le module fs mais retournant des promises

const fs = require('fs-promise');

fs.readFile('./log.tkt', { encoding: 'utf8' })
  .then(fileContent => {
    console.log(fileContent);
  })
  .catch(err => {
    console.error(err)
  });

Pour les amateurs de promise !!!

npm i fs-promise -S

http : appel http

Le module http va entre autres nous permettre d'effectuer des requêtes HTTP.

var http = require('http');

module.exports = {
    getPosts: function(){
        return new Promise((resolve,reject) => {
            http.get({
                host: 'myWebservice.org',
                path: '/posts'
            }, response => {
                // Continuously update stream with data
                var body = '';
                response.on('data', d => {
                    body += d;
                });
                response.on('end', () => {
                    resolve(JSON.parse(body));
                });
            });          
        })
    }
};

isomorphic-fetch

Ce module est une implémentation pour node de la méthode fetch du browser.

const fetch = require('isomorphic-fetch');

module.exports = {
  getPosts(){
    return fetch('http://www.mywebservice.org/post')
      .then(response => response.json())
  }
};
npm i fs-promise -S

path

  • path.normalize()

  • path.join()

  • path.resolve()

  • path.dirname()

  • ...

var path = require('path');

path.normalize('/foo/bar//baz/..')
//return /foo/bar/baz/'

path.resolve('/foo/bar', './baz')
// return /foo/bar/baz

Le module path permet de manipuler des chemin (absolu ou relatif).

util

  • util.inspect()

  • util.isArray()

  • util.inherit()

  • util.deprecate()

  • ...

const util = require('util');


util.inspect({name: 'foo'});
// toString

util.isArray(['foo','bar']); 
//return true

util.isDate('foo');
//return false

Le module util contient des méthodes utilitaires.

EventEmitter

Dans Node, beaucoup de modules sont capable d'émettre et d'écouter des évenements.

Ces modules sont des instances de la classe events.EventEmitter.

Comme par exemple les module fs et http.

EventEmitter : les méthodes

Toutes les instances de la classe EventEmitter héritent de ces méthodes :

  • addEventListener()
  • on()
  • once()
  • emit()
  • removeListener()
  • removeAllListener()

Les modules héritant de cette classe peuvent ainsi notifier le reste de l'application d'un changement d'état.

EventEmitter : Utilisation

const util = require('util');
const EventEmitter = require('events').EventEmitter;

class MyStream extends EventEmitter {

  constructor() {
    super();
  }

  write(data) {
    this.emit('data', data);
  }

}

const stream = new MyStream();
stream.on('data', data => {
  console.log(`Received data: ${data}`);
});
stream.write('It works!'); // Received data: 'It works!'

L'objet global process

process est une instance de EventEmitter.

Cet objet va nous permettre de définir ou récupérer les variables d’environnement.

De maniére générale cet objet permet de gérer le processus courant :

  • évènement (exit...)
  • exception

Publier un package sur npm

  • un répértoire contenant du code source et un fichier package.json complet.
  • une url Git définie dans le fichier package.json

Un package npm est :

Publier un package sur npm

Il ne reste ensuite plus que deux opérations.

S'authentifier

npm adduser

Publier

#Dans le repertoire du package
npm publish

Serveur web

 

Mise en place d'un serveur http

le package http, sert également à mettre en place un serveur http.

const http = require('http');
const url = require('url');

const server = http.createServer((req, res) => {

  const route = url.parse(req.url).pathname;

  console.log(route);  

  res.writeHead(200);

  res.end('Hello world');

});

server.listen(8080);

Gestion des routes

const http = require('http');

const url = require('url');

const server = http.createServer((req, res) => {
    const route = url.parse(req.url).pathname;
    console.log(page);

    res.writeHead(200, {"Content-Type": "text/plain"});

    switch(route){
        case '/':
            res.write('Welcome home !!!');
        break;
        case '/contact':
            res.write('Contact us');
        break;
        default:
            res.write('404');
        break;    
    }
});

server.listen(8080);

Querystring

const http = require('http');
const url = require('url');
const querystring = require('querystring');

const server = http.createServer((req, res) => {
    const route = url.parse(req.url).pathname;

    const params = querystring.parse(url.parse(req.url).query);

    res.writeHead(200, {'Content-Type': 'application/json'});

    switch(route){
        case '/posts':
            if(params.title){
                //do something
            }else{
                //do something else     
            }
        break;
        default:
            res.write('404');
        break;    
    }
});

server.listen(8080);

remarque ?

C'est légèrement verbeux !!!

Vite un framework

Heureusement , l'écosystème Node.js est déjà riche en frameworks et CMS

Frameworks :

CMS :

Express est à la base de tous ces outils (sauf ghost).

Express

Minimaliste, flexible

Framework standard

Fonctionne avec plusieurs moteur de template :

Très pratique pour réaliser des api REST.

  • jade
  • ejs
  • ...

Express : Mise en place

const express = require('express');
const app = express();

const sendHello = (req, res) => {
  res.send('Hello, World!');
};

app.get('/', sendHello);

app.listen(3000);
npm install express --save

Hello World !!!

Express : Routes dynamiques

const express = require('express');

const app = express();

app.get('/users/:id', (req, res) => {
    console.log(req.params.id);
    res.end(req.params.id);
});

app.listen(3000);

Nous pouvons egalement spécifier des routes dynamiques.

Il sera ensuite très facile de récuperer la valeur du paramètre.

Express : renvoyer du html

const express = require('express');
const app = express();

const html = `<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Hello</title>
    </head>
    <body>
       <p>Hello World</p>
    </body>
</html>`;

app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.send(html);
});
app.listen(3000);

Nous pouvons également renvoyer directement le contenu d'un fichier.

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.sendFile(__dirname + '/index.html');
});
app.listen(3000);

Express : le templating

Express nous permet d'utiliser un certains nombre de moteur de template.

  • jade
  • ejs
  • ...

Comportement similaire à du php.

Express : ejs

npm install ejs --save

Embeded JavaScript

const app = require('express')();
const ejs = require('ejs');

app.set('view engine', 'ejs');

app.get('/profile', (req, res) => {
  const user = {
    email: 'foo@bar.org',
    username: 'Mr Foo'
  };
  res.render('profile', {user});
});
app.listen(3000);
<!doctype html>
<html>
<head>
    <title>
        Profile
        <%= user.username %>
    </title>
</head>
<body>
<h1>
    <%= user.username %>
</h1>
<div>
    <a href="mailto:<%= user.email %>">Contact</a>
</div>
</body>
</html>

Les templates doivent se trouver dans le repertoire views.

./views/profile.ejs

./index.js

Boucles et conditions

const app = require('express')();

app.get('/profile', (req, res) => {
    var posts = [
        {
            title: 'premier post',
            content: 'du contenu'
        },
        {
            title: 'deuxième post',
            content: 'encore du contenu'
        }
    ];
    res.render('blog', {posts posts});
});
app.listen(3000);
<!doctype html>
<html>
<head>
    <title>
        Blog
    </title>
</head>
<body>
    <section>
    <% posts.forEach(function(post){ %>
        <article>
            <h1>
                <%= post.title %> 
            </h1>
            <p>
                <%= post.content %> 
            </p>
        </article>
    <% }) %>
    </section>
</body>
</html>

./views/blog.ejs

./index.js

Include

<footer>
    i'am a footer
</footer>
<!doctype html>
<html>
<head>
    <title>
        Blog
    </title>
</head>
<body>
    <%- partial('header') %>    
    <main>
        <!-- some content -->
    </main>
    <%- partial('footer') %>    
</body>
</html>

./views/blog.ejs

./views/footer.ejs

<header>
    i'am on top
</header>

./views/header.ejs

Express et les middlewares

middleware ???

Petits bouts de code qui réalisent des actions diverses.

Les middlewares traitent les requetes http et s'enchainent en se passant le contenu de la requête.

  • session
  • body parser
  • json web token
  • ...

Body-parser

Parse le contenu des requêtes http.

npm install body-parser --save
const app = require('express')();
const bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false }))

app.use(bodyParser.json())

app.use((req, res, next) => {
    res.setHeader('Content-Type', 'application/json')
    console.log(req.body);
    next();
});

app.post('/post',(req,res) =>{
    //faire quelque chose avec req.body
});
app.listen(3000);

Les sessions

npm install express-session --save
const app = require('express')();
const session = require('express-session');

app.use(session({ secret: 'my-secret', cookie: { maxAge: 60000 }}));

app.use((req, res, next) => {
  var sess = req.session
  if (sess.views) {
    sess.views++;
    res.setHeader('Content-Type', 'text/html');
    res.write('<p>views: ' + sess.views + '</p>');
    res.write('<p>expires in: ' + (sess.cookie.maxAge / 1000) + 's</p>');
    res.end();
  } else {
    sess.views = 1;
    res.end('welcome to the session demo. refresh!');
  }
});
app.listen(3000);

Permet de manipuler les cookies.

Compression (gzip)

npm install compression --save
const app = require('express')();
const compression = require('compression');

app.use(compression({
  filter(req, res) {
    if (req.headers['x-no-compression']) {
    // don't compress responses with this request header
      return false
    }

    // fallback to standard filter function
    return compression.filter(req, res)
  }
}))

app.listen(3000);

Permet la compression gzip de la page pour un envoi plus rapide au navigateur.

Servir des fichiers statiques

npm install serve-static --save
const app = require('express')();
const serveStatic = require('serve-static');

app.use(serveStatic('public/ftp', { index: ['index.html', 'index.htm'] }));
app.listen(3000);

permet de renvoyer des fichiers statiques contenus dans un dossier.

CRUD

var app = require('express')();

app.route('/api/posts')
.all((req, res, next) => {
  // sera executée avant toutes les autres
  // sorte de middleware spécifique à la route
})
.get((req, res) => {
    res.json([]);
})
.post((req, res) => {
  // create a post
})

app.route('/api/posts/:id')
.get((req, res) => {
    res.json({});
})
.put((req, res) => {
  // update the post
})
.delete((req, res) => {
  // remove the post
})

Servir une singlePage Application

const app = require('express')();
const session = require('express-session');

//... your middleware
//... your api routes

app.get('*', (req, res, err) => {
  res.sendfile(`${__dirname}/public/index.html`);
})

app.listen(3000);

Gestion des erreurs

const app = require('express')();
const session = require('express-session');

//... your middleware
//... your routes

app.use((req, res, err) => {
  console.error(err);
  res.status(err.status);
  res.json(err);
})

app.listen(3000);

Communication avec une base de données no-sql

 

En informatique, NoSQL (Not only SQL en anglais) désigne une catégorie de systèmes de gestion de base de données (SGBD) qui n'est plus fondée sur l'architecture classique des bases relationnelles. L'unité logique n'y est plus la table, et les données ne sont en général pas manipulées avec SQL.

Wikipédia

MongoDB

{
    "_id": ObjectId("4efa8d2b7d284dad101e4bc7"),
    "Nom": "DUMONT",
    "Prénom": "Jean",
    "Âge": 43
}

MongoDB permet de manipuler des objets structurés au format BSON (JSON binaire).

Des clés peuvent être ajoutées à tout moment "à la volée", sans reconfiguration de la base.

MongoDB VS SQL

On ne parle pas de table mais de collection.

Une collection contient des documents.

Un document est l'équivalent d'un enregistrement.

Pas de schéma, les documents peuvent avoir une structure différente au sein d'une même collection.

Tous les documents possèdent un attribut _id.

Mongoose

O.R.M. pour node.js et mongoDB.

  • Définition de schéma
  • fonction de validation
  • relations
  • hooks

Propose une API simple.

npm install --save mongoose

Connexion

const mongoose = require('mongoose')

mongoose.Promise = Promise

module.exports.connectDatabase = uri => {
  return new Promise((resolve, reject) => {
    mongoose.connection
      .on('error', error => reject(error))
      .on('close', () => console.log('connection closed'))
      .once('open', () => resolve(mongoose.connections[0]))

    mongoose.connect(uri)
  })
}

Schema : les types

  • String
  • Number
  • Date
  • Buffer
  • Boolean
  • Mixed
  • ObjectId
  • Array

Les types autorisés par mongoose sont :

Schéma

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const UserSchema = new Schema({
  email: {
    type: String,
    unique: true,
    required: 'email is required'
  },
  age: {
    type: Number
  },
  username: {
    type: String,
    unique: true
  },
  hobbies: {
    type: Array
  },
  createdAt: {
    type: Date,
    default: Date.now()
  },
  password: {
    type: String,
    unique: true,
    required: 'password is required'
  }
});

module.exports = mongoose.model('User', UserSchema);

CRUD : create

const mongoose = require('mongoose');
const User = mongoose.model('User');

//Persister une entité en base

const user = new User({
    email: 'dummy@email.com',
    password: 'dummyPassword'
})

user.save()
  .then(data => {
    console.log('user created');
  })
  .catch(err => {
    console.error(err);
  })

CRUD : retrieve

const mongoose = require('mongoose');
const User = mongoose.model('User');

//recuperer tous les enregistrements

User.find()
  .then(users => {
    console.log('users: ', users);
  })
  .catch(err => {
    console.error(err);
  })
const mongoose = require('mongoose');
const User = mongoose.model('User');


User.find({ email : 'dummy@email.com' })
  .then(users => {
    console.log('users: ', users);
  })
  .catch(err => {
    console.error(err);
  })

CRUD : retrieve one

const mongoose = require('mongoose');
const User = mongoose.model('User');


User.findOne({ email: 'dummy@email.com' })
  .then(user => {
    console.log('user: ', user);
  })
  .catch(err => {
    console.error(err);
  })

CRUD : update

const mongoose = require('mongoose');
const User = mongoose.model('User');

User.update({ email: 'dummy@email.com' },{ username : 'foo' })
  .then(data => {
    console.log('data: ', data);
  })
  .catch(err => {
    console.error(err);
  })

CRUD : delete

const mongoose = require('mongoose');
const User = mongoose.model('User');

//recuperer tous les enregistrements

User.remove({ email: 'dummy@email.com' })
  .then(() => {
    console.log('user removed');
  })
  .catch(err => {
    console.error(err);
  })

Requête complexe

User.find()
    .where('firstname').equals('foo')
    .where('age').gt(17).lt(66)
    .where('hobbies').in(['reading', 'code'])
    .limit(10)
    .sort('-age')
    .select('firstname email')
    .then(data => {
        
    })
    .catch(err => {

    });

Hooks

const mongoose = require('mongoose'),
const Schema = mongoose.Schema,
const UserSchema = new Schema({
      ...
});

UserSchema.pre('save',() => {
        
});

UserSchema.pre('update', () => {
        
});

module.exports = mongoose.model('User',UserSchema);

Relations

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema({
  ...
  comments: [{
    type: Schema.ObjectId,
    ref: 'Comments'
  }],
  author: {
    type: Schema.ObjectId,
    ref: 'User'
  }
});

module.exports = mongoose.model('User',PostSchema);

Relations: query

var mongoose = require('mongoose'),
    Post = mongoose.model('Post');

Post.findOne({ title: 'anAwesomePost' })
  .populate('comments')
  .then(posts => {
    
  })

Communication en temps réel avec socket.io

 

Socket.io

Permet de mettre en place une communication "real time".

npm install socket.io --save

Fonctionne même avec des vieux browsers (pré HTML5).

Web-socket ?

Web-socket Vs Ajax

Avec AJAX le client demande et le serveur répond.

Avec les web-sockets, le serveur peut "push" vers le client sans requête préalable.

Socket.io: les technos

Afin de supporter un très large eventail de browser socket.io utilise les technologies suivante coté client : 

  • Websocket
  • Adobe flash socket
  • AJAX long polling
  • ...

Du coup la plupart des browsers sont supporté même ie 5 ( sisi :) ).

Avec Express

const app = require('express')();
const http = require('http').Server(app);
const io = require('socket.io')(http);

app.get('/', (req, res) => {
  res.sendfile('index.html');
});

io.on('connection', socket => {
  console.log('a user connected');
});

http.listen(3000, () => {
  console.log('listening on *:3000');
});

Socket.io : le client

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Socket.io</title>
    </head>
 
    <body>
        <h1>Communication avec socket.io !</h1>

        <script src="/socket.io/socket.io.js"></script>
        <script>
            var socket = io.connect('http://localhost:8080');
        </script>
    </body>
</html>

Le fichier html servi par le serveur.

socket.io.js est servi automatiquement.

Socket.io : communication

io.sockets.on('connection', socket => {
  socket.emit('message', 'Vous êtes bien connecté !');
});
var socket = io.connect('http://localhost:8080');
socket.on('message', function(message) {
    console.log('new message ',message);
})

Le serveur

Le client

Socket.io : Hello world

io.sockets.on('connection', socket => {
    socket.on('hello', message => {
        console.log(message);
    });
});
var socket = io.connect('http://localhost:8080');
$('#hello').click(function () {
    socket.emit('hello', 'Hello World');
})

Le serveur

Le client

<button id="hello">Hello world</button>

Socket.io : Plusieurs clients

socket.emit envoie seulement le message au client courant.

Pour envoyer le message à tous les clients connectés il faut utiliser socket.broadcast().

Socket.io : Hello world à plusieurs

io.sockets.on('connection', socket => {
  socket.on('hello', message => {
    socket.broadcast.emit('hello',message);
  });
});
var socket = io.connect('http://localhost:8080');
$('#hello').click(function () {
    socket.emit('hello', 'Hello World');
})

socket.on('hello',function(message){
    console.log(message);        
})

Le serveur

Le client

<button id="hello">Hello world</button>

Variable de session

Afin d'identifier facilement nos clients, nous allons pouvoir utiliser des variables de sessions.

io.sockets.on('connection', socket => {
  socket.on('register', username => {
    socket.username = username;
  });
});

Socket.io : Namespace (room)

const chat = io
  .of('/chat')
  .on('connection', socket => {
    socket.emit('message','Welcome to the chat');
  });

var news = io
  .of('/news')
  .on('connection', socket => {
    socket.emit('lastnews', { news: 'lorem ipsum' });
  });
var chat = io.connect('http://localhost:8080/chat'),
    news = io.connect('http://localhost:8080/news');
  
chat.on('connect', function (message) {
    console.log(message);
});
  
news.on('lastnews', function (message) {
    console.log(message);
});

Tester son aplication

Les modules

Mocha est le framework de test que nous allons utiliser.

Supertest va nous permetrre de tester notre api REST.

Should est la bibliothèque d'assertion.

Should.js

npm install should --save--dev
const should = require('should');

const user = {
    name: 'Heisenberg',
    hobbies: ['cooking', 'chemistry']
};

user.should.have.property('name', 'tj');
user.should.have.property('hobbies').with.lengthOf(2);

Simple librairie d'assertion.

Mocha.js

Les tests se trouvent dans le repertoire test.

Le fichier principal se nomme test.js.

Reprenez la structure de votre application pour vos tests.

npm i mocha -D

Mocha : hooks

describe('my tests', () => {
  before(() => {
    // runs before all tests in this block
  })
  after(() => {
    // runs after all tests in this block
  })
  beforeEach(() => {
    // runs before each test in this block
  })
  afterEach(() => {
    // runs after each test in this block
  })
  // test cases
})

Mocha : test

const should = require('should');

const thingz = ['foo','bar'];

describe('thingz', () => {
    it('should have 2 elements', () => {
        thingz.length.should.be(2);
    })
})
const should = require('should');

describe('main tests', () => {
    describe('nested tests', () => {
        it('should test something', () => {

        })
    })
})

Lancer les tests

mocha --recursive

Mocha + Supertest + express 

const request = require('supertest');
const should = require('should');
const app = require('../app').app;


describe('addition', () => {
 it('should return a post', done => {
   request(app)
     .get('/api/post/1')
     .expect(200)
     .end((err, res) => {
       should.not.exist(err);
       res.data.should.have.property('content');
       done();
     });
 });
});

Supertest nous permet de tester notre api rest codée avec express.

The End

Réalisé par Charles Jacquin

Formation Node.js

By AdapTeach

Formation Node.js

Formation Node.js

  • 1,525