SlideShare ist ein Scribd-Unternehmen logo
1 von 26
Downloaden Sie, um offline zu lesen
Bonnes pratiques de
développement avec Node.js
              François Zaninotto
     @francoisz http://github.com/fzaninotto
Node.js ne suffit pas
et vous ne pouvez pas réinventer la roue
Organisation du code
        Objectif: é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
// main app.js
var 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' }));
});

// Routes
app.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);
// dashboard app
var 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'));
});

// Routes
app.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);
}
// dashboard app
var 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'));
});

// Routes
app.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);
}
<!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>
<!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>
Standards
     Felix's Node.js Style Guide
 (http://nodeguide.com/style.html)


Object-Oriented Programming FTW


     Domain-Driven Design!
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'));
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'));
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'));
Canaux de communication
                Dans un module
                                  Entre applications Client / Serveur
               ou une application

                   Appels de
Notifications                           Events            socket.io
                   méthodes

Echanges de        Appels de                            XMLHTTP
                                     API HTTP
  données          méthode                               Request

Traitements
                    Promise            AMQP              Mentir*
asynchrones
// server-side
var 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());
});
// 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>
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
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');
    });
  });
});
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
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();
           })
        })
     })
})
var nock = require('nock');

var couchdb = nock('http://myapp.iriscouch.com')
  .get('/users/1')
  .reply(200, {
     _id: "123ABC",
     _rev: "946B7D1C",
     username: 'pgte',
     email: 'pedro.teixeira@gmail.com'}
  );
Projet open-source
Configurable
  app.configure()   pas compatible avec un SCM
  lorenwest/node-config (ou flatiron/nconf)

Extensible
  Custom events
  Plugin architecture
// in main app.js
path.exists('./plugins/index.js', function(exists) {
  if (exists) {
     require('./plugins').init(app, io, config);
  };
});

// in plugins/index.js
exports.init = function(app, io, config) {
  require('./console').init();
}
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);
   });
};
Questions ?


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

Weitere ähnliche Inhalte

Was ist angesagt?

Was ist angesagt? (20)

Containers & Dependency in Ember.js
Containers & Dependency in Ember.jsContainers & Dependency in Ember.js
Containers & Dependency in Ember.js
 
soft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.jssoft-shake.ch - Hands on Node.js
soft-shake.ch - Hands on Node.js
 
"Auth for React.js APP", Nikita Galkin
"Auth for React.js APP", Nikita Galkin"Auth for React.js APP", Nikita Galkin
"Auth for React.js APP", Nikita Galkin
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2
 
HTML5: friend or foe (to Flash)?
HTML5: friend or foe (to Flash)?HTML5: friend or foe (to Flash)?
HTML5: friend or foe (to Flash)?
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 
Django + Vue, JavaScript de 3ª generación para modernizar Django
Django + Vue, JavaScript de 3ª generación para modernizar DjangoDjango + Vue, JavaScript de 3ª generación para modernizar Django
Django + Vue, JavaScript de 3ª generación para modernizar Django
 
Java script for web developer
Java script for web developerJava script for web developer
Java script for web developer
 
Mastering Spring Boot's Actuator with Madhura Bhave
Mastering Spring Boot's Actuator with Madhura BhaveMastering Spring Boot's Actuator with Madhura Bhave
Mastering Spring Boot's Actuator with Madhura Bhave
 
Workshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte IIIWorkshop 14: AngularJS Parte III
Workshop 14: AngularJS Parte III
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
TestWorks conf Dry up your angularjs unit tests using mox - Mike Woudenberg
TestWorks conf Dry up your angularjs unit tests using mox - Mike WoudenbergTestWorks conf Dry up your angularjs unit tests using mox - Mike Woudenberg
TestWorks conf Dry up your angularjs unit tests using mox - Mike Woudenberg
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
And the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack SupportAnd the Greatest of These Is ... Rack Support
And the Greatest of These Is ... Rack Support
 
Asynchronous programming done right - Node.js
Asynchronous programming done right - Node.jsAsynchronous programming done right - Node.js
Asynchronous programming done right - Node.js
 
Good karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with KarmaGood karma: UX Patterns and Unit Testing in Angular with Karma
Good karma: UX Patterns and Unit Testing in Angular with Karma
 
Viking academy backbone.js
Viking academy  backbone.jsViking academy  backbone.js
Viking academy backbone.js
 
FwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.jsFwDays 2021: Metarhia Technology Stack for Node.js
FwDays 2021: Metarhia Technology Stack for Node.js
 
Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011Ruby - Design patterns tdc2011
Ruby - Design patterns tdc2011
 
Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)Asynchronous Programming FTW! 2 (with AnyEvent)
Asynchronous Programming FTW! 2 (with AnyEvent)
 

Ähnlich wie Bonnes pratiques de développement avec Node js

How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
Ben Lin
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
Igor Bronovskyy
 
Dependency Management with RequireJS
Dependency Management with RequireJSDependency Management with RequireJS
Dependency Management with RequireJS
Aaronius
 
Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for Joomla
Luke Summerfield
 
Node js introduction
Node js introductionNode js introduction
Node js introduction
Alex Su
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & Tricks
Hjörtur Hilmarsson
 
Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
Chris Alfano
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
Tom Croucher
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
Ting Lv
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
Skills Matter
 

Ähnlich wie Bonnes pratiques de développement avec Node js (20)

Future Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NETFuture Decoded - Node.js per sviluppatori .NET
Future Decoded - Node.js per sviluppatori .NET
 
Spine.js
Spine.jsSpine.js
Spine.js
 
How and why i roll my own node.js framework
How and why i roll my own node.js frameworkHow and why i roll my own node.js framework
How and why i roll my own node.js framework
 
node.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.ionode.js and the AR.Drone: building a real-time dashboard using socket.io
node.js and the AR.Drone: building a real-time dashboard using socket.io
 
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
09 - express nodes on the right angle - vitaliy basyuk - it event 2013 (5)
 
Dependency Management with RequireJS
Dependency Management with RequireJSDependency Management with RequireJS
Dependency Management with RequireJS
 
JS everywhere 2011
JS everywhere 2011JS everywhere 2011
JS everywhere 2011
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
Javascript Frameworks for Joomla
Javascript Frameworks for JoomlaJavascript Frameworks for Joomla
Javascript Frameworks for Joomla
 
Asynchronous Interfaces
Asynchronous InterfacesAsynchronous Interfaces
Asynchronous Interfaces
 
Node js introduction
Node js introductionNode js introduction
Node js introduction
 
Javascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & TricksJavascript MVC & Backbone Tips & Tricks
Javascript MVC & Backbone Tips & Tricks
 
Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011Jarv.us Showcase — SenchaCon 2011
Jarv.us Showcase — SenchaCon 2011
 
Writing robust Node.js applications
Writing robust Node.js applicationsWriting robust Node.js applications
Writing robust Node.js applications
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Rich Portlet Development in uPortal
Rich Portlet Development in uPortalRich Portlet Development in uPortal
Rich Portlet Development in uPortal
 
前端MVC 豆瓣说
前端MVC 豆瓣说前端MVC 豆瓣说
前端MVC 豆瓣说
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Primefaces Nextgen Lju
Primefaces Nextgen LjuPrimefaces Nextgen Lju
Primefaces Nextgen Lju
 
Tools for Making Machine Learning more Reactive
Tools for Making Machine Learning more ReactiveTools for Making Machine Learning more Reactive
Tools for Making Machine Learning more Reactive
 

Mehr von Francois Zaninotto

Mehr von Francois Zaninotto (12)

Vous aimez les legos ? React est fait pour vous !
Vous aimez les legos ? React est fait pour vous !Vous aimez les legos ? React est fait pour vous !
Vous aimez les legos ? React est fait pour vous !
 
GraphQL, l'avenir du REST ?
GraphQL, l'avenir du REST ?GraphQL, l'avenir du REST ?
GraphQL, l'avenir du REST ?
 
La blockchain, quand l'individu sert au collectif... malgré lui
La blockchain, quand l'individu sert au collectif... malgré luiLa blockchain, quand l'individu sert au collectif... malgré lui
La blockchain, quand l'individu sert au collectif... malgré lui
 
Le jeu vidéo à la rescousse du web
Le jeu vidéo à la rescousse du webLe jeu vidéo à la rescousse du web
Le jeu vidéo à la rescousse du web
 
Frameworks : A history of violence
Frameworks : A history of violenceFrameworks : A history of violence
Frameworks : A history of violence
 
Php 100k
Php 100kPhp 100k
Php 100k
 
La migration continue vers Symfony
La migration continue vers SymfonyLa migration continue vers Symfony
La migration continue vers Symfony
 
La programmation asynchrone... et les pates
La programmation asynchrone... et les patesLa programmation asynchrone... et les pates
La programmation asynchrone... et les pates
 
Ce bon vieux propel
Ce bon vieux propelCe bon vieux propel
Ce bon vieux propel
 
Symfony2 meets propel 1.5
Symfony2 meets propel 1.5Symfony2 meets propel 1.5
Symfony2 meets propel 1.5
 
Developing for Developers
Developing for DevelopersDeveloping for Developers
Developing for Developers
 
Simplify your professional web development with symfony
Simplify your professional web development with symfonySimplify your professional web development with symfony
Simplify your professional web development with symfony
 

Kürzlich hochgeladen

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Kürzlich hochgeladen (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Developing An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of BrazilDeveloping An App To Navigate The Roads of Brazil
Developing An App To Navigate The Roads of Brazil
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 

Bonnes pratiques de développement avec Node js

  • 1. Bonnes pratiques de développement avec Node.js François Zaninotto @francoisz http://github.com/fzaninotto
  • 2.
  • 3.
  • 4. Node.js ne suffit pas et vous ne pouvez pas réinventer la roue
  • 5. Organisation du code Objectif: é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
  • 6. // main app.js var 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' })); }); // Routes app.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);
  • 7. // dashboard app var 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')); }); // Routes app.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); }
  • 8. // dashboard app var 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')); }); // Routes app.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); }
  • 9. <!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>
  • 10. <!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>
  • 11. Standards Felix's Node.js Style Guide (http://nodeguide.com/style.html) Object-Oriented Programming FTW Domain-Driven Design!
  • 12. 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'));
  • 13. 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'));
  • 14. 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'));
  • 15. Canaux de communication Dans un module Entre applications Client / Serveur ou une application Appels de Notifications Events socket.io méthodes Echanges de Appels de XMLHTTP API HTTP données méthode Request Traitements Promise AMQP Mentir* asynchrones
  • 16. // server-side var 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()); });
  • 17. // 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>
  • 18. 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
  • 19. 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'); }); }); });
  • 20. 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
  • 21. 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(); }) }) }) })
  • 22. var nock = require('nock'); var couchdb = nock('http://myapp.iriscouch.com') .get('/users/1') .reply(200, { _id: "123ABC", _rev: "946B7D1C", username: 'pgte', email: 'pedro.teixeira@gmail.com'} );
  • 23. Projet open-source Configurable app.configure() pas compatible avec un SCM lorenwest/node-config (ou flatiron/nconf) Extensible Custom events Plugin architecture
  • 24. // in main app.js path.exists('./plugins/index.js', function(exists) { if (exists) { require('./plugins').init(app, io, config); }; }); // in plugins/index.js exports.init = function(app, io, config) { require('./console').init(); }
  • 25. 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); }); };
  • 26. Questions ? François Zaninotto @francoisz http://github.com/fzaninotto