Pré-requis / installation

MongoDB

Voir Installation de MongoDB

nodejs

# apt-get install nodejs

express

# npm install express -g

Pour debian, il faut créer un lien symbolique de nodejs vers node.

# cd /usr/bin
ln -s /usr/bin/nodejs /usr/bin/node

Express Generator

# npm install -g express-generator

répertoire de l'application

Créer un répertoire pour notre application, exemple: ~/app1

$ mkdir ~/app1 &&cd ~/app1

Un serveur express de base

On doit préparer le dossier app1 pour express.

$ express -c stylus
npm install -d
# patch debian sid (2013-12-05)
sed -i "s/doctype 5/doctype html/" views/layout.jade

# note que sur debian sid (2013-12-05) on obtient une erreur de doctype avec la commande node app  et en visitant http://localhost:3000/.
# 500 Error: /home/ymorin/learning/nodejs/serverem/views/index.jade:5<br/> 3| block content<br/> 4| h1= title<br/> > 5| p Welcome to #{title}<br/><br/>`doctype 5` is deprecated, you must now use `doctype html`

On ne peut pas s'inspirer de simple-express.js sur le tutoriel puisque "express.createServer()" est déprécié

Le serveur est maintenant créé à l'aide de http.createServer(app).

Modèle des articles (en mémoire)

Dans le fichier articleprovider-memory.js

var articleCounter = 1;
var ArticleProvider = function(){};
ArticleProvider.prototype.dummyData = [];

ArticleProvider.prototype.findAll = function(callback) {
  callback( null, this.dummyData )
};

ArticleProvider.prototype.findById = function(id, callback) {
  var result = null;
  for(var i=0; i<this.dummyData.length; i++) {
    if( this.dummyData[i]._id == id ) {
      result = this.dummyData[i];
      break;
    }
  }
  callback(null, result);
};

ArticleProvider.prototype.save = function(articles, callback) {
  var that = this;
  if( !articles || articles.length===undefined) {
    articles = [articles];
  }

  articles.forEach(function(article) {
    article._id = articleCounter++;
    article.created_at = new Date();

    if( article.comments === undefined ) {
      article.comments = [];
    }

    article.comments.forEach(function(comment) {
      comment.created_at = new Date();
    });
    that.dummyData.push(article);
  });
  callback(null, articles);
};

/* Lets bootstrap with dummy data */
new ArticleProvider().save([
  {title: 'Post one', body: 'Body one', comments:[{author:'Bob', comment:'I love it'}, {author:'Dave', comment:'This is rubbish!'}]},
  {title: 'Post two', body: 'Body two'},
  {title: 'Post three', body: 'Body three'}
], function(error, articles){});

exports.ArticleProvider = ArticleProvider;

Dans le fichier app.js

/**
 * Module dependencies.
 */

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

// AJOUTER CETTE LIGNE
var ArticleProvider = require('./articleprovider-memory').ArticleProvider;

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

// AJOUTER CETTE LIGNE
var articleProvider = new ArticleProvider();

app.get('/', routes.index);
app.get('/users', user.list);
// AJOUTER CES LIGNES
app.get('/articles', function(req, res) {
    articleProvider.findAll(function(error, docs) {
       res.send(docs);
    });
});
// FIN AJOUT

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

Redémarrer le serveur (CTRL-C) node app et visiter la page: http://localhost:3000/articles

Template view/articles.jade

Dans le fichier views/articles.jade

extends layout

block content
  h1= title
  #articles
      - each article in articles
        div.article
          div.created_at= article.created_at
          div.title
              a(href="/blog/"+article._id)!= article.title
          div.body= article.body

Dans le fichier app.js, remplacer la route /articles par:

app.get('/articles', function(req, res) {
    articleProvider.findAll(function(error, docs) {
       res.render('articles', {
           title: 'Blogues',
           articles: docs
       });
    });
});

Tester: http://localhost:3000/articles

Formulaire d'ajout (blog/new)

Dans le fichier views/blog_new.jade

extends layout

block content
  h1= title
  form( method="post")
      div
          div
              span Title :
              input(type="text", name="title", id="editArticleTitle")
          div
              span Body :
              textarea( name="body", rows=20, id="editArticleBody")
          div#editArticleSubmit
              input(type="submit", value="Ajouter")

Dans le fichier app.js, ajouter les routes:

app.get('/blog/new', function(req, res) {
    res.render('blog_new', {
        title: 'Nouvel article'
    });
});

app.post('/blog/new', function(req, res){
    articleProvider.save({
        title: req.param('title'),
        body: req.param('body')
    }, function( error, docs) {
        res.redirect('/articles')
    });
});

Tester: http://localhost:3000/blog/new

Ajouter mongodb

Dans le fichier package.json, ajouter dans les dependencies:

,"mongodb": ">= 0.9.6-7"

Installer:

npm install -d

articleprovider-mongodb.js

var Db = require('mongodb').Db;
var Connection = require('mongodb').Connection;
var Server = require('mongodb').Server;
var BSON = require('mongodb').BSON;
var ObjectID = require('mongodb').ObjectID;

var ArticleProvider = function(host, port) {
  this.db = new Db('node-mongo-blog', new Server(host, port, {auto_reconnect: true, safe: false}, {}));
  this.db.open(function(){});
};

ArticleProvider.prototype.getCollection = function(callback) {
  this.db.collection('articles', function(error, article_collection) {
    if( error ) callback(error);
    else callback(null, article_collection);
  });
};

ArticleProvider.prototype.findAll = function(callback) {
    this.getCollection(function(error, article_collection) {
      if( error ) callback(error)
      else {
        article_collection.find().toArray(function(error, results) {
          if( error ) callback(error)
          else callback(null, results)
        });
      }
    });
};


ArticleProvider.prototype.findById = function(id, callback) {
    this.getCollection(function(error, article_collection) {
      if( error ) callback(error)
      else {
        // before 201604 it was: _id: article_collection.db.bson_serializer.ObjectID.createFromHexString(id)
        article_collection.findOne({_id: new ObjectID.createFromHexString(id)}, function(error, result) {
          if( error ) callback(error)
          else callback(null, result)
        });
      }
    });
};

ArticleProvider.prototype.save = function(articles, callback) {
    this.getCollection(function(error, article_collection) {
      if( error ) callback(error)
      else {
        if( typeof(articles.length)=="undefined")
          articles = [articles];

        articles.forEach(function(article) {
          article.created_at = new Date();
          if( article.comments === undefined ) article.comments = [];
          article.comments.forEach(function(comment) {
            comment.created_at = new Date();
          });
        });

        article_collection.insert(articles, function() {
          callback(null, articles);
        });
      }
    });
};

exports.ArticleProvider = ArticleProvider;

Modifier dans le fichier app.js, les lignes suivantes:

var ArticleProvider = require('./articleprovider-mongodb').ArticleProvider;

var articleProvider = new ArticleProvider('localhost', 27017);

Afficher un article

Dans app.js, ajouter la règle suivante:

app.get('/blog/:id', function(req, res) {
    articleProvider.findById(req.params.id, function(error, article) {
        res.render('blog_show',
        { 
            title: article.title,
            article:article
        });
    });
});

Ajouter le fichier views/blog_show.jade

extends layout

block content
  h1= title
  div.article
      div.created_at= article.created_at
      div.title= article.title
      div.body= article.body
      - each comment in article.comments
        div.comment
          div.person= comment.person
          div.comment= comment.comment
      div
        form( method="post", action="/blog/addComment")
          input( type="hidden", name="_id", value=article._id.toHexString())
          div
            span Author :
            input( type="text", name="person", id="addCommentPerson")
          div
            span Comment :
            textarea( name="comment", rows=5, id="addCommentComment")
          div#editArticleSubmit
            input(type="submit", value="Ajouter")

Ajouter un commentaire

app.js

app.post('/blog/addComment', function(req, res) {
    articleProvider.addCommentToArticle(req.param('_id'), {
        person: req.param('person'),
        comment: req.param('comment'),
        created_at: new Date()
       } , function( error, docs) {
           res.redirect('/blog/' + req.param('_id'))
       });
});

articleprovider-mongodb.js

ArticleProvider.prototype.addCommentToArticle = function(articleId, comment, callback) {
  this.getCollection(function(error, article_collection) {
    if( error ) callback( error );
    else {
      article_collection.update(
        {_id: article_collection.db.bson_serializer.ObjectID.createFromHexString(articleId)},
        {"$push": {comments: comment}},
        function(error, article){
          if( error ) callback(error);
          else callback(null, article)
        });
    }
  });
};

Autre outils

npm install nodemon -g # nodemon permet de redémarrer l'application lorsqu'on fait des changements.

npm install monk --save # monk permet d'écrire/enregistrer dans MongoDB.