26
Bonnes pratiques de développement avec Node.js François Zaninotto @francoisz http://github.com/fzaninotto

Bonnes pratiques de développement avec Node js

Embed Size (px)

DESCRIPTION

Présentation faite au #ParisJS du 16 avril 2012

Citation preview

Page 1: Bonnes pratiques de développement avec Node js

Bonnes pratiques de développement avec Node.js

François Zaninotto@francoisz http://github.com/fzaninotto

Page 2: Bonnes pratiques de développement avec Node js
Page 3: Bonnes pratiques de développement avec Node js
Page 4: Bonnes pratiques de développement avec Node js

Node.js ne suffit paset vous ne pouvez pas réinventer la roue

Page 5: Bonnes pratiques de développement avec Node js

Organisation du codeObjectif: éviter le code spaghetti

Utiliser un framework pour les applis web

visionmedia/express (ou viatropos/tower)

Eviter la course aux callbacks

caolan/async (ou kriskowal/q)

Inclure des routes et monter des sous-applications

«Fat model, Skinny controller»

Modules de service pour éviter un modèle trop fat

Page 6: Bonnes pratiques de développement avec Node js

// main app.jsvar express = require('express');var app = module.exports = express.createServer();

app.configure(function(){ app.use(app.router); // these middlewares are required by some of the mounted apps app.use(express.bodyParser()); app.use(express.methodOverride()); app.use(express.cookieParser()); app.use(express.session({ secret: 'qdfegfskqdjfhskjdfh' }));});

// Routesapp.use('/api', require('./app/api/app'));app.use('/dashboard', require('./app/dashboard/app'));app.get('/', function(reaq, res) { res.redirect('/dashboard/events');});

app.listen(3000);

Page 7: Bonnes pratiques de développement avec Node js

// dashboard appvar express = require('express');var app = module.exports = express.createServer();

app.configure(function(){ app.use(app.router); app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.static(__dirname + '/public'));});

// Routesapp.get('/events', function(req, res) { res.render('events', { route: app.route });});app.get('/checks', function(req, res) { res.render('checks', { route: app.route, info: req.flash('info')});});//...if (!module.parent) { app.listen(3000);}

Page 8: Bonnes pratiques de développement avec Node js

// dashboard appvar express = require('express');var app = module.exports = express.createServer();

app.configure(function(){ app.use(app.router); app.set('views', __dirname + '/views'); app.set('view engine', 'ejs'); app.use(express.static(__dirname + '/public'));});

// Routesapp.get('/events', function(req, res) { res.render('events', { route: app.route });});app.get('/checks', function(req, res) { res.render('checks', { route: app.route, info: req.flash('info')});});//...if (!module.parent) { app.listen(3000);}

Page 9: Bonnes pratiques de développement avec Node js

<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8"> <title>Uptime</title> <link rel="stylesheet" href="<%= route %>/stylesheets/bootstrap.css"> <link rel="stylesheet" href="<%= route %>/stylesheets/style.css"> </head> <body> <div class="navbar"> <div class="navbar-inner"> <div class="container"> <a class="brand" href="<%= route %>/events">Uptime</a> <ul class="nav pull-left"> <li><a href="<%= route %>/events">Events</a></li> <li><a href="<%= route %>/checks">Checks</a></li> <li><a href="<%= route %>/tags">Tags</a></li> </ul> </div> </div> </div>

Page 10: Bonnes pratiques de développement avec Node js

<!DOCTYPE html><html lang="en"> <head> <meta charset="utf-8"> <title>Uptime</title> <link rel="stylesheet" href="<%= route %>/stylesheets/bootstrap.css"> <link rel="stylesheet" href="<%= route %>/stylesheets/style.css"> </head> <body> <div class="navbar"> <div class="navbar-inner"> <div class="container"> <a class="brand" href="<%= route %>/events">Uptime</a> <ul class="nav pull-left"> <li><a href="<%= route %>/events">Events</a></li> <li><a href="<%= route %>/checks">Checks</a></li> <li><a href="<%= route %>/tags">Tags</a></li> </ul> </div> </div> </div>

Page 11: Bonnes pratiques de développement avec Node js

Standards

Felix's Node.js Style Guide(http://nodeguide.com/style.html)

Object-Oriented Programming FTW

Domain-Driven Design!

Page 12: Bonnes pratiques de développement avec Node js

var mongoose = require('mongoose'), Schema = mongoose.Schema, async = require('async');

var Check = new Schema({ name : String , type : String , url : String , interval : { type: Number, default: 60000 } , maxTime : { type: Number, default: 1500 } , tags : [String] , lastChanged : Date , lastTested : Date , isUp : Boolean , uptime : { type: Number, default: 0 } , downtime : { type: Number, default: 0 }});Check.plugin(require('../lib/lifecycleEventsPlugin'));

Page 13: Bonnes pratiques de développement avec Node js

var mongoose = require('mongoose'), Schema = mongoose.Schema, async = require('async');

var Check = new Schema({ name : String , type : String , url : String , interval : { type: Number, default: 60000 } , maxTime : { type: Number, default: 1500 } , tags : [String] , lastChanged : Date , lastTested : Date , isUp : Boolean , uptime : { type: Number, default: 0 } , downtime : { type: Number, default: 0 }});Check.plugin(require('../lib/lifecycleEventsPlugin'));

Page 14: Bonnes pratiques de développement avec Node js

var mongoose = require('mongoose');var Schema = mongoose.Schema;var async = require('async');

var Check = new Schema({ name : String, type : String, url : String, interval : { type: Number, default: 60000 }, maxTime : { type: Number, default: 1500 }, tags : [String], lastChanged : Date, lastTested : Date, isUp : Boolean, uptime : { type: Number, default: 0 }, downtime : { type: Number, default: 0 },});Check.plugin(require('../lib/lifecycleEventsPlugin'));

Page 15: Bonnes pratiques de développement avec Node js

Canaux de communication

Dans un module ou une application Entre applications Client / Serveur

Notifications

Echanges de données

Traitements asynchrones

Appels de méthodes

Events socket.io

Appels de méthode

API HTTP XMLHTTP Request

Promise AMQP Mentir*

Page 16: Bonnes pratiques de développement avec Node js

// server-sidevar socketIo = require('socket.io');var CheckEvent = require('./models/checkEvent');

var io = socketIo.listen(app);

CheckEvent.on('postInsert', function(event) { io.sockets.emit('CheckEvent', event.toJSON());});

Page 17: Bonnes pratiques de développement avec Node js

// client-side<!DOCTYPE html><html lang="en"> <head> <script src="/socket.io/socket.io.js"></script> <script>var socket = io.connect('http://'+location.hostname);</script> </head> <body> <div class="navbar"><ul id="counts"></ul></div> <script> $(document).ready(function() { var updateCounts = function() { $.getJSON('/api/check/count', function(count) { // display counts in navbar }); }; updateCounts(); socket.on('CheckEvent', function() { updateCounts(); $('#count').fadeOut().fadeIn().fadeOut().fadeIn(); }); }); </script> </body></html>

Page 18: Bonnes pratiques de développement avec Node js

Gestions des erreurs

try {} catch {} ne marche pas en asynchrone

function (err, results) est la signature standard des callbacks asynchrones

"Leave early"

Utiliser les codes d’erreur HTTP pour les APIs

Page 19: Bonnes pratiques de développement avec Node js

app.delete('/check/:id', function(req, res, next) { Check.findOne({ _id: req.params.id }, function(err, check) { if (err) { return next(err); } if (!check) { return next(new Error('No check with id ' + req.params.id)); } check.remove(function(err2){ if (err2) { req.flash('error', 'Error - Check not deleted'); res.redirect('/checks'); return; } req.flash('info', 'Check has been deleted'); res.redirect('/checks'); }); });});

Page 20: Bonnes pratiques de développement avec Node js

Tests Unitaires

Librairies (presque) standard

Assertions : visionmedia/should.js (ou node/assert)

TDD : visionmedia/mocha (ou caolan/node-unit)

BDD : visionmedia/mocha (ou mhevery/jasmine-node)

L’asynchrone se teste aussi très bien

Pas besoin de mocker quand on peut monkey-patcher

Page 21: Bonnes pratiques de développement avec Node js

describe('Connection', function(){ var db = new Connection , tobi = new User('tobi') , loki = new User('loki') , jane = new User('jane');

beforeEach(function(done){ db.clear(function(err){ if (err) return done(err); db.save([tobi, loki, jane], done); }); })

describe('#find()', function(){ it('respond with matching records', function(done){ db.find({ type: 'User' }, function(err, res){ if (err) return done(err); res.should.have.length(3); done(); }) }) })})

Page 22: Bonnes pratiques de développement avec Node js

var nock = require('nock');

var couchdb = nock('http://myapp.iriscouch.com') .get('/users/1') .reply(200, { _id: "123ABC", _rev: "946B7D1C", username: 'pgte', email: '[email protected]'} );

Page 23: Bonnes pratiques de développement avec Node js

Projet open-source

Configurable

app.configure() pas compatible avec un SCMlorenwest/node-config (ou flatiron/nconf)

Extensible

Custom events

Plugin architecture

Page 24: Bonnes pratiques de développement avec Node js

// in main app.jspath.exists('./plugins/index.js', function(exists) { if (exists) { require('./plugins').init(app, io, config); };});

// in plugins/index.jsexports.init = function(app, io, config) { require('./console').init();}

Page 25: Bonnes pratiques de développement avec Node js

module.exports = exports = function lifecycleEventsPlugin(schema) { schema.pre('save', function (next) { var model = this.model(this.constructor.modelName); model.emit('preSave', this); this.isNew ? model.emit('preInsert', this) : model.emit('preUpdate', this); this._isNew_internal = this.isNew; next(); }); schema.post('save', function() { var model = this.model(this.constructor.modelName); model.emit('postSave', this); this._isNew_internal ? model.emit('postInsert', this) : model.emit('postUpdate', this); this._isNew_internal = undefined; }); schema.pre('remove', function (next) { this.model(this.constructor.modelName).emit('preRemove', this); next(); }); schema.post('remove', function() { this.model(this.constructor.modelName).emit('postRemove', this); });};

Page 26: Bonnes pratiques de développement avec Node js

Questions ?

François Zaninotto@francoisz http://github.com/fzaninotto