---
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 **Stretch**, **Buster** et **Bullseye**

## Installation

En temps normal, nous préconisons l'installation de NodeJS avec les paquets Debian produits et distribuées par NodeSource pour deux raisons :

1/ NodeJS a un cycle de publication de version plus rapide que celui de Debian, il est donc nécessaire de passer par un dépot tiers pour obtenir les versions plus récentes.

2/ Dans certains situations, même avec des versions similaires, les paquets de Debian ne semblent pas avoir les mêmes optimisations que ceux de NodeSource, ce qui peut être penalisant dans certains cas d'usage.

> **Note** : On va avoir tendence à préférer les versions *paires* de NodeJS qui sont des version à support long terme (LTS)

### Utilisation de NodeSource (**Stretch**, **Buster** et **Bullseye**)

L'url du dépot à utiliser suit le modèle `https://deb.nodesource.com/<VERSION_NODEJS> <DISTRIBUTION_LINUX> main`

Avec `<DISTRIBUTION_LINUX>` étant le nom de code de la distribution : 

* *stretch*
* *buster*
* *bullseye*

Et `<VERSION_NODEJS>` étant la branche de version souhaitée :
 
* *node_10.x*
* *node_12.x*
* *node_14.x*
* *node_16.x*
* *node_18.x*
* *node_20.x* : Version LTS actuelle

Exemple pour avoir la version 18.x en Debian 11 (bullseye)

~~~
# apt install apt-transport-https
# echo "deb https://deb.nodesource.com/node_18.x $(awk -F= '/VERSION_CODENAME/{print $2}' /etc/os-release) 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
# chmod 644 /etc/apt/trusted.gpg.d/nodesource.asc
# apt update && apt install nodejs

$ node -v
v18.15.0
~~~

### Note pour Buster et nodejs 10.x

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.24.0, 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.

~~~
# apt install apt-transport-https
# 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
# chmod 644 /etc/apt/trusted.gpg.d/nodesource.asc
# apt update && apt install nodejs

$ node -v
v10.24.1
~~~


## Modules npm

### Utilisateur

#### Mise en place

Afin qu'un utilisateur puisse directement utiliser les binaires installés par ses paquets NPM, il faut s'assurer que :

1. `$HOME/node_modules/.bin` est dans son `$PATH` 
2. `/home` ne soit pas monté avec l'option `noexec`

Ça peut se faire avec les commandes suivantes :

~~~(sh)
# (umask 022; echo 'PATH="$HOME/node_modules/.bin:$PATH"' > /etc/profile.d/npm.sh)
# findmnt /home
~~~~

#### Utilisation

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.

Cas normal : Installation via un paquet Debian (pour tous les utilisateurs) :

~~~
# 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
# dos2unix /etc/apt/trusted.gpg.d/yarn.asc
# chmod 644 /etc/apt/trusted.gpg.d/yarn.asc
# apt update && apt install yarn
~~~

Cas spécifique : installation par NPM (juste pour un utilisateur)

~~~
$ npm 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 avec npm. Il existe des depots Debian mais il ne sont pas mis a jours depuis des année et ne fonctionnent pas.

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` - Liste les applications connues/actives 
* `$ pm2 start APP_NAME|ID` - Démarre une application
* `$ pm2 start xxx.js` - Démarre une (nouvelle) application
* `$ pm2 start all` - Démarre toutes les applications
* `$ pm2 stop all` - Stoppe toutes les applications
* `$ 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
* `$ pm2 monit` - Lance une console pour surveiller toutes les applications en live : logs, ressources
* `$ pm2 logs APP_NAME|ID` - Suivre les logs d'une application
* `$ pm2 logs` - Suivre les logs de toutes les applications

Il existe un module [pm2-logrotate](https://github.com/keymetrics/pm2-logrotate) qui permet d'automatiser la rotation des logs générés par PM2.

Les logs sont situés dans le répertoire `~/.pm2/logs/` et/ou le fichier `~/.pm2/pm2.log`.
Plus d'infos sur <https://pm2.keymetrics.io/docs/usage/log-management/>

### 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` :

~~~ { .ini }
[Unit]
Description=Example.com
[Service]
# Run the service as user jdoe and group jdoe
User=jdoe
Group=jdoe

WorkingDirectory=/home/jdoe/www
# A script that start the application, should NOT fork and exit (because of the service type being `simple`).
# (forking and waiting for the node process to die is OK but exec() or equivalent is generally better)
ExecStart=/home/jdoe/www/start

# Restart if the process die or if it is killed by something else than systemd
Restart=always

# For logging
StandardOutput=/home/jdoe/log/nodejs-access.log
StandardError=/home/jdoe/log/nodejs-error.log
SyslogIdentifier=example.com

# 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;
}
~~~


## `nodenv`

`nodenv` est un outil permettant de faire cohabiter plusieurs versions de `node`
sur un même système, à la manière de `virtualenv` pour `python`. Entre autre,
cela permet à utilisateur d'utiliser une version de `node` différente que celle
installé globalement par le gestionnaire de paquet.


<a id="org38d98b3"></a>

### Installation de `nodenv`

Version abbrégé des instructions du [README](https://github.com/nodenv/nodenv) pour une instllation manuelle :

    $ git clone https://github.com/nodenv/nodenv.git ~/.nodenv
    $ echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.profile
    $ ~/.nodenv/bin/nodenv init >> ~/.profile 2>&1
    $ exit # Puis rouverez un login shell

L&rsquo;installation des versions de `node` n&rsquo;est pas gérée par nativement par
`nodenv`, on peut bien sur le faire manuellement mais c&rsquo;est plus commode
d&rsquo;utiliser `node-build` un plugin de `nodenv`. Ce plugin permet la commande
`nodenv install 10.13.0` qui va installer `node` en version `10.13.0`. Ce plugin
s&rsquo;installe avec les commandes suivantes :

    $ mkdir -p "$(nodenv root)"/plugins
    $ git clone https://github.com/nodenv/node-build.git "$(nodenv root)"/plugins/node-build


### Utilisation de `nodenv`

Après avoir installé `nodenv` et `node-build` vous pouvez installer autant de
version de `node` que vous souhaitez. Pour ce faire, avec un `/tmp` monté en
`noexec` on change le dossier temporaire à un emplacement où l&rsquo;execution est
autorisé (dans notre cas /home), on execute :

    $ mkdir -p "$HOME/tmp"
    $ TMPDIR="$HOME/tmp" nodenv install 14.16.1

La version de `node` que `nodenv` va activer est configuré par dossier en
fonction de la version contenue dans le fichier `.node-version`. Si on veux
activer la version de `node` pour le dossier courrant il faut executer `nodenv
local 14.16.1` qui va y créer le fichier `.node-version` contenant la version
voulue. On peut vérifier ça avec :

    $ cat .node-version
    14.16.1
    $ node -v
    v14.16.1


### Débogage

Si quelque chose se passe mal avec votre installation de `nodenv` vous pouvez
utiliser le plugin [`nodenv-doctor`](https://github.com/nodenv/nodenv-installer/blob/master/bin/nodenv-doctor) pour avoir une vue globale de son
installation :

    $ curl -fsSL https://github.com/nodenv/nodenv-installer/raw/master/bin/nodenv-doctor | bash
    Checking for `nodenv' in PATH: /usr/local/bin/nodenv
    Checking for nodenv shims in PATH: OK
    Checking `nodenv install' support: /usr/local/bin/nodenv-install (node-build 3.0.22-4-g49c4cb9)
    Counting installed Node versions: none
      There aren't any Node versions installed under `~/.nodenv/versions'.
      You can install Node versions like so: nodenv install 2.2.4
    Auditing installed plugins: OK

## 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**.

### ENOSPC: System limit for number of file watchers reached

~~~
$ npm run build
[...]
Error: ENOSPC: System limit for number of file watchers reached, watch [...]
[...]
~~~

Lorsque _npm_ lève une erreur `ENOSPC: System limit for number of file watchers reached` cela signifie que le nombre maximum de fichiers observés avec inotify (syscall kernel), utilisés par wabpack par exemple, a été atteint, il faut alors augmenter la valeur de `fs.inotify.max_user_watches` (défaut = 8192) par sysctl.
