Animée par Charles Jacquin
Rendez vous sur le site officiel et suivez les instructions.
Pour Ubuntu il existe un PPA.
Node.js est fournis avec npm, un gestionnaire de paquet.
npm install <nom_du_module> #installation dans le repertoire courant
npm install <nom_du_module> -g #installation globale
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 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
Node utilise v8 le moteur JavaScript de chromium.
Non bloquant.
Moteur Javascript de Chrome
Compilation "Just In Time"
Le JavaScript est construit autour
de la notion d' événement.
La même chose en Java
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
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);
}
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
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
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
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);
});
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){
});
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';
console
Le même objet que dans le browser
__filename
Le fichier courant
__dirname
Le répertoire courant
setTimeout() et clearTimeout()
setInterval() et clearInterval()
Fonctionne comme dans le browser
Node.js contient de base un certain nombre de paquet.
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.
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
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.
Le module fs, permet la manipulation de fichier.
Simples "wrapper" autour des fonctions standard UNIX.
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.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.
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
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));
});
});
})
}
};
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.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.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.
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.
Toutes les instances de la classe EventEmitter héritent de ces méthodes :
Les modules héritant de cette classe peuvent ainsi notifier le reste de l'application d'un changement d'état.
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!'
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 :
Un package npm est :
Il ne reste ensuite plus que deux opérations.
S'authentifier
npm adduser
Publier
#Dans le repertoire du package
npm publish
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);
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);
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 !!!
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).
Minimaliste, flexible
Framework standard
Fonctionne avec plusieurs moteur de template :
Très pratique pour réaliser des api REST.
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
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.
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 nous permet d'utiliser un certains nombre de moteur de template.
Comportement similaire à du php.
npm install ejs --save
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
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
<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
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.
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);
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.
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.
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.
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
})
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);
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);
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
Installer mongoDB http://www.mongodb.org/downloads
{
"_id": ObjectId("4efa8d2b7d284dad101e4bc7"),
"Nom": "DUMONT",
"Prénom": "Jean",
"Âge": 43
}
Des clés peuvent être ajoutées à tout moment "à la volée", sans reconfiguration de la base.
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.
O.R.M. pour node.js et mongoDB.
Propose une API simple.
npm install --save mongoose
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)
})
}
Les types autorisés par mongoose sont :
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);
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);
})
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);
})
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);
})
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);
})
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);
})
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 => {
});
const mongoose = require('mongoose'),
const Schema = mongoose.Schema,
const UserSchema = new Schema({
...
});
UserSchema.pre('save',() => {
});
UserSchema.pre('update', () => {
});
module.exports = mongoose.model('User',UserSchema);
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);
var mongoose = require('mongoose'),
Post = mongoose.model('Post');
Post.findOne({ title: 'anAwesomePost' })
.populate('comments')
.then(posts => {
})
Permet de mettre en place une communication "real time".
npm install socket.io --save
Fonctionne même avec des vieux browsers (pré HTML5).
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.
Afin de supporter un très large eventail de browser socket.io utilise les technologies suivante coté client :
Du coup la plupart des browsers sont supporté même ie 5 ( sisi :) ).
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');
});
<!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.
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
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.emit envoie seulement le message au client courant.
Pour envoyer le message à tous les clients connectés il faut utiliser socket.broadcast().
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>
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;
});
});
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);
});
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.
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.
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
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
})
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
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.
Réalisé par Charles Jacquin