Aller au contenu

Serveur Node.js

Node.js est une plate-forme de développement serveur en JavaScript. Node.js propose une programmation évènementielle non bloquante, ce qui permet de créer des applications temps-réel, tout en mutualisant le code JS entre client et serveur.

L’architecture de la plateforme est composée de :

  • l’API Node qui est un ensemble de modules prêts à l’emploi : client et serveur HTTP, accès au système de fichiers, gestion des chemins sur l’OS, etc. Ces modules sont basés sur l’API CommonJS, afin de rendre le code d’un module interopérable sur différentes plateformes (navigateur, serveur, etc.). Il est bien entendu possible de créer ses propres modules CommonJS.
  • la machine virtuelle V8 qui est un compilateur JavaScript (celui de Chrome)
  • l’interpréteur Node qui permet d’exécuter le tout

Voici un exemple simpliste de serveur HTTP, qui retourne toujours le contenu du fichier index.html situé à la racine du projet, et ce, peu importe la requête de l’utilisateur

let fs   = require('fs');
let http = require('http').createServer((req, res) => {
  fs.readFile(__dirname + '/index.html', (err, data) => {
    if (err) {
      res.writeHead(500);
      return res.end('Error loading index.html');
    }
    res.writeHead(200);
    res.end(data);
  });
}).listen(80);
$ node nom_du_fichier

npm#

npm est le gestionnaire phare de modules Node. Il est souvent installé par défaut lors de l’installation de Node. Voici quelques commandes :

  • npm init crée un nouveau package.json
  • npm (i)nstall [<package>] installe le package demandé ou tous les packages listés dans package.json (dependencies et devDependencies)
  • npm (up)date [<package>] mets à jour
  • npm start exécute le script start
  • npm run(-script) <script> exécute le script demandé
  • npm adduser crée un nouvel utilisateur
  • npm publish publie un nouveau module

Depuis peu, yarn (Facebook) est également très utilisé.

Websockets#

Le prototcole Websockets est une extension à HTTP, qui permet de créer une connexion ouverte bi-directionelle entre le serveur et les clients. Le serveur peut donc à tout moment envoyer une information au client, sans que celui-ci ne refasse de requête. Les Websockets sont à la base du Push et rendent obsolètes les autres techniques comme l’AJAX long-polling.

Node.js simplifie énormément l’utilisation des Websockets, et notamment le module socket.io. Voici un chat simpliste mais temps-réel :

Exercice - Création d'un chat#

Partie serveur:

let fs   = require('fs');
let http = require('http').createServer(function (req, res) {
  fs.readFile(__dirname + '/index.html',
    function (err, data) {
      if (err) {
        res.writeHead(500);
        return res.end('Error loading index.html');
      }

      res.writeHead(200);
      res.end(data);
    }
  );
});
let io   = require('socket.io')(http);

http.listen(80);

io.on('connection', function (socket) {
  socket.emit('welcome', { message: 'Vous êtes connecté au chat !' });
  socket.on('message', function (data) {
    console.log('Message: ' + data);
    socket.broadcast.emit('message', {message: data});
  });
});

Partie cliente:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <style>
    </style>
  </head>
<body>
  <h1>CHAT</h1>
  <div id="websocket">
    <form action="#" id="chatbox" >
      <input type="text" name="message" placeholder="Votre message" />
      <input type="submit" />
    </form>
    <div id="messages"></div>
  </div>
  <script src="/socket.io/socket.io.js"></script>
  <script>
  let socket = io('http://localhost');
  socket.on('welcome', function (data) {
    afficheMessage(data.message);
  });
  socket.on('message', function (data) {
    afficheMessage(data.message);
  });
  let form = document.getElementById('chatbox');
  form.addEventListener('submit', function (e) {
    socket.emit('message', form.elements['message'].value);
    afficheMessage(form.elements['message'].value);
    form.elements['message'].value = '';
    e.preventDefault();
  });

  function afficheMessage (msg) {
    let message = document.createElement('p');
    message.innerHTML = msg;
    document.getElementById('messages').appendChild(message);
  }
  </script>
</body>
</html>

Le code est basé sur socket.io, un module Node.js qui permet de gérer les WebSockets de manière simplifiée. Avant toute chose, il faut donc l'installer via npm (gestionnaire de modules Node.js), via la ligne de commande, déplacez-vous dans votre dossier, puis :

$ npm install socket.io

Pour lancer le serveur Node.js :

$ node server.js

Affichez la page http://localhost dans plusieurs onglets et/ou plusieurs navigateurs. Améliorez le chat:

  • Demandez le nom de la personne avant connection au chat
  • Modifiez les messages pour afficher le nom
  • Affichez un message quand les personnes se connectent ou se déconnectent
  • Ajoutez la fonctionnalité ("un utilisateur est en train de taper")

Express#

Express est un module npm qui nous apporte les fonctionnalités fondamentales d’un serveur web (gestion des requêtes, passage de paramètres, intégration de base de données, templating, etc.). Il simplifie et sécurise donc le développement d’un serveur Node

Base de données#

Il existe une multitude de systèmes de base de données sous Node, la plupart ayant des modules prévus pour Express.

Exercice - Node + Express + Mongo#

Le but de cet exercice est de créer une visualisation basique des restaurants de New-York.

  • Créez une application express à l’aide du générateur d’application Express
  • Créez un conteneur Docker pour cette base Docker mongoDB. Il faudra penser à exposer le port 27017 de ce conteneur pour que votre application node puisse y accéder.

docker run -d --name mongotsi -p 27017:27017 mongo

  • Installez le module mongodb pour Node : npm install mongodb --save

Première étape, la base de données :

  • Voici les données sources : restaurants.json

  • Copiez le fichier source dans le conteneur : docker cp restaurants.json mongotsi:/

  • Insérez les données : docker exec -ti mongotsi mongoimport --db test --collection restaurants --drop --file /restaurants.json

Pour pouvoir faire des requêtes spatiales, n'oubliez pas de créer un index spatial sur l'attribut location:

  • Connectez-vous à Mongo Shell: docker exec -ti mongotsi mongosh
  • Executez db.restaurants.createIndex({ location: '2dsphere' })

Seconde étape, création du serveur

  • Créez une route express nommée data
  • Connectez-vous à votre base de données Mongodb, (voir comment se connecter, puis comment chercher dans une collection), et retournez l’intégralité du résultat en JSON
  • Testez l’URL de votre service depuis votre navigateur (attention, le fichier est volumineux)
  • Pour limiter nos données, ajoutez à votre route un paramètre :bbox qui sera une chaine de caractères avec les latitudes/longitudes séparées par des virgules du rectangle de sélection respectant ce format : southwest_lng,southwest_lat,northeast_lng,northeast_lat
  • Traitez cette chaine puis modifiez votre requête pour sélectionner uniquement les restaurants inclus dans cette bbox. Voir https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/index.html. Attention au type (String vs. Number) dans les spécifications du $box .

Ensuite, coté client (avec ou sans Vue)

  • Créez une nouvelle carte Leaflet, centrée sur New-York (lat/lng 40.7/-73.9, zoom 16) dans la vue index.pug. Pug)
  • Au chargement, récupérez la bbox actuelle de votre carte en chaine de caractères (bounds.toBBoxString())
  • Faites une requête vers /data/<bbox> et affichez les restaurants visibles (markers) (attention à l'ordre des coordonnées)

Enfin

  • Lorsque l’on déplace la carte, on charge les restaurants de la nouvelle bbox, mais
    • On n’affiche que les restaurants qui ne sont pas déjà présents (pour éviter les doublons)
    • On enlève les restaurants qui ne sont plus visibles dans la bbox actuelle
    • Pensez aux nouveautées ES6

Ressource supplémentaire : http://duspviz.mit.edu/web-map-workshop/leaflet_nodejs_postgis/