---
categories: web
title: Howto NodeJS
...

* Documentation : <https://nodejs.org/dist/latest-v10.x/docs/api/>
* Rôle Ansible : <https://forge.evolix.org/projects/ansible-roles/repository/show/nodejs>

[Node.js](https://nodejs.org/) est une implémentation libre en langage Javascript orientée pour les applications réseau événementielles. Node.js intègre une bibliothèque HTTP permettant de faire tourner un serveur web, notamment en utilisant le protocole [WebSocket](https://fr.wikipedia.org/wiki/WebSocket).

Cette documentation est compatible avec Debian **Jessie**, **Stretch** et **Buster**

## Installation

### Buster

Nous préconisons l'installation des paquets Debian distribués par NodeSource (version 10.x LTS). En effet, ceux présent dans Debian Buster, bien qu'en version 10.15.2, ne semblent pas présenter les mêmes optimisations que ceux distribués par NodeSource. Ainsi, sur les temps de démarrage du moteur NodeJS, on constate jusqu'à 600ms de plus contre des temps inférieurs à 100ms pour la version NodeSource, ce qui peut être pénalisant dans certains cas d'usage.

~~~
# echo "deb http://deb.nodesource.com/node_10.x buster main" >> /etc/apt/sources.list.d/nodesource.list
# wget https://deb.nodesource.com/gpgkey/nodesource.gpg.key -O /etc/apt/trusted.gpg.d/nodesource.asc
# apt update && apt install nodejs

$ nodejs -v
v10.16.3
~~~


### Jessie/Stretch

Nous préconisons l'installation des paquets Debian distribués par NodeSource (version 10.x LTS). En effet, ceux présent dans Debian Stretch sont assez anciens (Version: 4.x). Idem pour ceux dans les dépôts de Debian Jessie (Version: 0.10)

~~~
# echo "deb http://deb.nodesource.com/node_10.x stretch main" >> /etc/apt/sources.list.d/nodesource.list
# wget https://deb.nodesource.com/gpgkey/nodesource.gpg.key -O /etc/apt/trusted.gpg.d/nodesource.asc
# apt update && apt install nodejs

$ nodejs -v
v10.16.3
~~~


### Version 12.x (Prochaine version LTS)

Pour avoir Node.js 12.X on utilisera la source suivante :

~~~
# echo "deb http://deb.nodesource.com/node_12.x buster main" >> /etc/apt/sources.list.d/nodesource.list
~~~

## Modules npm

### Utilisateur

En tant qu'utilisateur, on peut installer des modules **npm**, par exemple :

~~~
$ npm version
{ npm: '5.6.0',
  ares: '1.10.1-DEV',
  cldr: '31.0.1',
  http_parser: '2.7.0',
  icu: '59.1',
  modules: '57',
  nghttp2: '1.25.0',
  node: '8.9.4',
  openssl: '1.0.2n',
  tz: '2017b',
  unicode: '9.0',
  uv: '1.15.0',
  v8: '6.1.534.50',
  zlib: '1.2.11' }

$ npm install ping
/home/jdoe
└─┬ ping@0.2.2
  ├── q@1.5.1
  └── underscore@1.8.3

$ npm list
/home/jdoe└─┬ ping@0.2.2
  ├── q@1.5.1
  └── underscore@1.8.3
~~~

### Global

Certains modules peuvent aussi être installés de manière globale.
Et ainsi rendre la commande disponible via /usr/bin/<nom_module> et les bibliothèques dans `/usr/lib/node_modules`.

~~~
# npm install -g npm
# chown -R root: /usr/lib/node_modules
# chmod -R 755 /usr/lib/node_modules
~~~

### Yarn

Yarn est un gestionnaire de dépendances pour NodeJS : <https://yarnpkg.com/fr/>
Il utilise le même registre de paquets que NPM mais pas le même mécanisme de résolution de l'arbre des dépendances.

Installation par NPM :

~~~
# npm install yarn
~~~

Installation via un paquet Debian :

~~~
# echo "deb http://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
# wget https://dl.yarnpkg.com/debian/pubkey.gpg -O /etc/apt/trusted.gpg.d/yarn.asc
# apt update && apt install yarn
~~~

### PM2

[PM2 (Process Manager 2)](https://pm2.io) est un système de gestion de processus en production. Il peut s'assurer du bon fonctionnement du (ou des) processus applicatif, collecter les logs, faire du load balancing, etc...

La version "Runtime" est peut être installée simplement avec npm, ou via un paquet Debian :


~~~
# echo "deb https://packagecloud.io/Keymetrics/pm2/debian/ stretch main" > /etc/apt/sources.list.d/Keymetrics_pm2.list 
# wget -qO - https://packagecloud.io/Keymetrics/pm2/gpgkey | apt-key add -
# apt update && apt install pm2
~~~

Une application pourra ensuite être lancée avec la commande `pm2 start index.js` 

~~~
root@cd57a5f23ead:/node-js-getting-started# pm2 start index.js
[PM2] Starting /node-js-getting-started/index.js in fork_mode (1 instance)
[PM2] Done.
┌──────────┬────┬──────┬───────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name │ id │ mode │ pid   │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
├──────────┼────┼──────┼───────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ index    │ 0  │ fork │ 17494 │ online │ 0       │ 0s     │ 0%  │ 19.5 MB   │ root │ disabled │
└──────────┴────┴──────┴───────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘
 Use `pm2 show <id|name>` to get more details about an app
~~~

On peut aussi utiliser un fichier de configuration (ou d'écosystème), qui va permettre d'ajuster certains paramètres comme les variables d'environnement ou la méthode de lancement de l'application. [Plus de détails dans la documentation officielle](https://pm2.io/doc/en/runtime/guide/ecosystem-file/)

Utiliser PM2 avec des comptes utilisateurs applicatifs : 

1. *Supprimer/Commenter* la ligne `export PM2_HOME=/etc/pm2` dans */etc/default/pm2* - **Important**, sinon pm2 va s'obstiner a essayer d'utiliser /etc/pm2
2. Utiliser la commande `pm2 startup -u app_user --hp /home/app_user/` pour installer une unité systemd pour la daemon pm2 de l'utilisateur
3. Démarrer pm2 pour l'utilisateur : `systemctl start pm2-app_user.service`

Quelques commandes utiles : 

* `$ pm2 ls` - Lister les applications connues/actives 
* `$ pm2 start APP_NAME|ID` - Démarrer une application
* `$ pm2 start xxx.js` - Démarrer une (nouvelle) application
* `$ pm2 save` - Sauvegarde la configuration en cours d'exécution. C'est ce qui sera relancé en cas de redémarrage de la machine 
* `$ pm2 ressurect` - Redémarre tous les process qui étaient précédament en cours d'exécution


### systemd

On peut activer une unité [systemd](HowtoSystemd) pour faire tourner un service en Node.js.

Par exemple, via `/etc/systemd/system/jdoe.js.service` :

~~~
[Unit]
Description=Example.com
[Service]
WorkingDirectory=/home/jdoe/www
ExecStart=/home/jdoe/www/start
Restart=always
StandardOutput=/home/jdoe/log/nodejs-access.log
StandardError=/home/jdoe/log/nodejs-error.log
SyslogIdentifier=example.com
User=jdoe
Group=jdoe
# Allow many incoming connections
LimitNOFILE=infinity

# Allow core dumps for debugging
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
~~~

## proxy HTTP

Si l'application Node.js tourne sur le port 4000, voici la configuration standard pour Nginx :

~~~
location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
~~~

## Websocket

Il est classique qu'une application Node.js utilise le protocole [Websocket](https://fr.wikipedia.org/wiki/WebSocket) qui permet d'ouvrir une sorte de connexion TCP au travers de HTTP pour avoir un canal de communication bidirectionnel et en temps réel avec le serveur. En fonction du module utilisé l'URL de connexion sera différente mais dans le cas du module _socket.io_ cela se fait en utilisant une requête HTTP GET vers une adresse du type _/socket.io/?xxxx_ avec les entêtes _Connection: Upgrade_ et _Upgrade: websocket_ qui va provoquer un code HTTP 101 (Switching Protocol) et l'ouverture d'une websocket.

Si l'on veut proxyfier cela avec Apache, il faut activer le module Apache [mod_proxy_wstunnel](https://httpd.apache.org/docs/2.4/mod/mod_proxy_wstunnel.html) :

~~~
# a2enmod proxy_wstunnel
~~~

avec une configuration du type :

~~~
RewriteCond %{REQUEST_URI}  ^/socket.io            [NC]
RewriteCond %{QUERY_STRING} transport=websocket    [NC]
RewriteRule /(.*)           ws://127.0.0.1:4000/$1 [P,L]
ProxyPass         /  http://127.0.0.1:4000/
ProxyPassReverse  /  http://127.0.0.1:4000/
~~~


## FAQ

###  Permission denied

~~~
$ npm run dev
[…]
sh: 1: node_modules/cross-env/dist/bin/cross-env.js: Permission denied
[…]
~~~

Lorsque _npm_ lève une erreur _Permission denied_ alors que tout semble exister avec les bons droits, il faut vérifier que la partition courante ne soit pas montée en **noexec**.
