Node.js
Cet article est en construction: un auteur est en train de le modifier.
En principe, le ou les auteurs en question devraient bientôt présenter une meilleure version.
Introduction
Node.js (également abrégé en Node par la suite) est un interprète du langage JavaScript qui permet d’utiliser JavaScript en dehors des navigateurs web, notamment dans le « côté-serveur », les desktops ou les « embedded devices ». Les caractéristiques principales de Node concernent le fonctionnement basé sur les événements et l’architecture « non-blocking » des mécanismes d’input/output (I/O). Grâce à ces particularités, Node est surtout utilisé dans le développement d’applications web « scalables », c’est-à-dire qui permettent de moduler les ressources hardware nécessaires en fonction du nombre d’utilisateurs et/ou de la puissance computationnelle nécessaire.
Objectifs de cette page
Cette page a des finalités pédagogiques sur la sensibilisation à l’utilisation de JavaScript dans d’autres environnements que le navigateur web, elle ne propose donc pas l’illustration exhaustive des caractéristiques techniques de Node.js. Les concepts et exemples illustrés sont donc limités à des environnements de test et/ou contrôlés par les développeurs et ne doivent pas être utilisés dans des situations ou la fiabilité et la sécurité sont primordiales. Pour comprendre le contenu de cette page la lecture des pages suivantes et conseillée :
Avertissement de sécurité
Contrairement à JavaScript côté-client dont l’exécution est contrôlée/limitée par les navigateurs web, Node.js peut avoir accès au système d’exploitation sur lequel il est installé. Ceci signifie que si vous installez Node.js sur votre ordinateur, Node.js peut potentiellement interagir avec des fichiers de système. Il faut donc faire beaucoup plus attention avec Node.js par rapport à JavaScript côté-client même en phase de développement/test ! Il y a le risque de corrompre ou effacer des fichiers importants même au niveau du fonctionnement du système !
L’écosystème Node.js
Node.js est une sorte d’écosystème qui permet aux développeurs d’utiliser JavaScript dans des fonctions de I/O telles que :
- Lire/Écrire des fichiers
- Accéder et/ou manipuler des bases de données
- Créer des applications réseaux comme des server web ou des streaming audio/vidéo
- Etc.
Pour obtenir cela, Node.js met à disposition des développeurs des « modules » qui sont déjà disponibles avec l’interprète ou qui peuvent être gérés grâce à npm, un gestionnaire de bibliothèques installé automatiquement avec Node.js. Ce gestionnaire permet d’ajouter facilement des « extensions » qui peuvent être téléchargées et utilisées :
- Dans des applications Node (e.g. express)
- En tant qu’outils pour effectuer des tâches liées au développement telles que des tests (e.g. karma), gestions de bibliothèques JavaScript et/ou CSS (e.g. bower), génération de squelettes pour applications (e.g. yeoman), etc.
Quelques mots sur la ligne de commande
Pour utiliser Node.js il faut utiliser très souvent la ligne de commande. Il y a différentes possibilités pour ouvrir une ligne de commande et cela dépend notamment du système d’exploitation utilisé, mais les commandes illustrées par la suite devraient marcher dans toute ligne de commande. Voici une liste de logiciels de ligne de commande pour les principaux systèmes d’exploitation :
- Pour Windows utiliser Windows PowerShell
- Pour Mac utiliser le Terminal
- Pour Linux utiliser l’une des lignes de commandes disponibles selon la version.
Position dans l’arborescence du système
Lorsque vous utilisez la ligne de commande de votre ordinateur il faut être très attentifs au dossier dans lequel vous vous trouvez, parce que c’est au niveau de ce dossier qui vont s’exécuter des éventuelles actions d’Input/Output générées par Node. Le dossier dans lequel vous vous trouvez s’affiche normalement sur la gauche de votre ligne de commande, sinon elle peut être récupérée avec la commande pwd
.
Quelques commandes de gestion de fichiers en ligne de commande
Vous pouvez également « naviguer » à travers les dossiers directement à l’intérieur de la ligne de commande, ainsi qu’obtenir des informations sur le contenu du dossier.
Avec la commande cd
vous pouvez vous déplacer dans d’autres dossier avec des adresses relatives ou absolus :
cd Documents
se déplace dans un dossier « Documents » contenu à l’intérieur de la position actuellecd ../
se déplace à un niveau supérieur par rapport à la position actuellecd C:/Users/votrelogin/Music
se déplace dans la position absolue du dossier Music
Évitez de travailler en ligne de commande dans des dossiers de système de votre système d’exploitation (e.g. C:/Windows/System) ou directement dans des dossiers racines comme par exemple le dossier C:/ en Windows. Il est mieux de travailler dans de sous-dossiers créés exprès pour tester de scripts Node.js.
Pour vous orienter dans les chemins de vos dossiers, vous pouvez lire le contenu d’un dossier avec la commande ls
qui affiche tous les fichiers et dossier contenu dans la position actuelle.
De plus, vous pouvez également utiliser des commandes pour créer, renommer ou effacer des dossiers ou des fichiers directement en ligne de commande. Ces commandes ne sont pas l’objet de cette page, donc on se limitera à l’exemple de la commande mkdir
qui permet de créer un nouveau dossier :
mkdir test
Cette commande crée un dossier nommé « test » à l’intérieur de la position actuelle de votre ligne de commande.
Installation de Node.js
L’installation de Node est très simple pour des finalités de développement et de test, mais plus complexe – au moins en ce moment (novembre 2015) – pour les environnements de production, qui ne seront pas traités dans cette page.
Node.js est disponible à travers le site officiel. Le download est disponible directement sur la page d’accueil, qui suggère directement la version adaptée à l’ordinateur de l’utilisateur, ou dans la page « Downloads » qui permet de choisir parmi les différentes versions disponibles.
Node est disponible pour Windows, Mac et Linux, ainsi que d’autres systèmes. Pour les systèmes Windows et Mac, Node met à disposition des versions avec « installer » qui représente la manière la plus simple pour ajouter Node à votre ordinateur. Il faut choisir :
- La version Windows Installer, avec extension .msi, pour Windows
- La version Macintosh Installer, avec extension .pkg, pour Mac
Ensuite, il suffit de suivre les étapes du programme d’installation. Les réglages suggérés sont adaptés pour la plupart des nécessités, il ne faut donc pas faire des changements normalement. Pour les installations Windows il faut juste être attentifs à la nécessité d’ajouter Node.js au PATH ainsi que la commande « node » sera disponible partout dans le système. Cette option est cochée en automatique dans les réglages suggérés, il faut donc juste éviter de la décocher :
La commande node
Pour contrôler que Node a bien été installé, ouvrez la ligne de commande (voir plus haut dans la page) et tapez directement le code :
node -v
La version installée de Node.js devrait s’afficher sur la ligne de commande. Voici le résultat pour la Windows PowerShell avec Node.js version 4.2.2. Faites attention avec Linux parce que parfois la commande associée n’est pas node mais peut être nodejs.
Console Node (REPL)
Lorsque vous tapez tout simplement node
, par contre, vous avez accès à la console de Node.js qui est tout à fait similaire à la console utilisée dans les navigateurs web. Ce type de console est également appelé « Read-eval-print loop » (REPL) parce qu’elle permet de :
- Lire (i.e. Read) du code
- Évaluer (i.e. Eval) les expressions
- Afficher (i.e. Print) le résultat
Vous pouvez donc saisir directement des commandes JavaScript dans la ligne de commande après le >
:
Pour sortir de la modalité console de Node.js tapez deux fois sur Ctrl + C
. Cette commande est utile pour sortir de toute opération en ligne de commande en général.
Exécuter le code dans un fichier JavaScript
La commande node
accepte également un ou plusieurs arguments, dont le premier est la référence à un fichier.js contenant du code, et les suivants sont des arguments qui sont passés au script :
node [fichier.js] [arg1, arg2, …]
Pour essayer vous pouvez créer un fichier script.js dans un dossier avec le simple code suivant :
//Code dans un fichier script.js console.log(‘Script Node.js’) ;
Ensuite il faut accéder à ce dossier avec la ligne de commande (voir plus haut dans la page) et saisir :
node script.js
Si on passe des arguments à la commande, ceux-ci seront accessibles à l’intérieur d’un script grâce à un array nommé process.argv
qui se compose de la manière suivante :
- process.argv[0] = node
- process.argv[1] = nom du fichier avec le script (e.g. script.js)
- process.argv[2] = premier argument passé après le nom du script
- process.argv[3] = deuxième argument passé après le nom du script
- …
Voici le code d’un fichier script.js qui écrit dans la console un message avec l’argument passé après le nom du script :
//Code dans un fichier script.js var nom = process.argv[2]; console.log("Hello " + nom);
Pour obtenir un message de hello il faut donc écrire dans la console :
node script.js votrenom
La commande npm
La commande npm
peut être utilisée pour plusieurs finalités qui vont au-delà des objectifs de cette page. Pour l’occasion, nous limiterons son intérêt à la fonction d’installation des modules Node.js. L’installation peut se faire à deux niveaux :
- Installation en locale dans la position actuelle en ligne de commande
- Installation global dans le système
En phase de développement/test il est souvent utile d’installer les modules de manière globale ainsi qu’ils soient disponibles à tout endroit dans le système. Voici le code pour installer le module bower
qui est très utile pour gérer des bibliothèques JavaScript/CSS côté frontend :
npm install -g bower
Pour une installation locale il faut tout simplement enlever le paramètre -g
. Dans ce cas les fichiers seront installés dans un répertoire node_modules dans la position actuelle de la ligne de commande.
Comprendre Node.js
Même si Node.js utilise JavaScript, il ne suffit pas de connaître JavaScript pour comprendre et savoir utiliser Node.js « out of the box ». La connaissance des bases de JavaScript (i.e. les fondamentaux du langage) est une condition indispensable mais pas suffisante pour écrire du code avec Node. En d’autres termes, si sur une échelle de 1 à 10 vous avez une connaissance de JavaScript de 10, cela ne signifie pas que vous aurez automatiquement une connaissance de 10 de Node.js. Par contre, si vous avez une connaissance de 4 en JavaScript, il est pratiquement impossible que vous ayez une connaissance supérieure à 4 en Node.js.
Comparaison avec JavaScript côté-client
Un parallèle avec JavaScript dans les navigateurs peut aider à comprendre le concept de base de Node.
L’objet window vs. les modules Node.js
On peut en effet affirmer que si JavaScript côté client permet un certain contrôle sur le navigateur web des utilisateurs grâce à l’objet global window, Node.js permet un grand contrôle sur l’ensemble de l’environnement hôte, c’est-à-dire le dispositif sur lequel « tourne » Node.js. Si, avec JavaScript côté-client, le développeur peut utiliser des propriétés et des méthodes de l’objet window pour déterminer certains comportements de l’application, avec Node.js il pourra utiliser les propriétés et les méthodes d’autres types d’objets, c’est-à-dire des modules, qui permettent d’interagir avec le système et ses composantes.
Par exemple le module fs
(i.e. File System) est un module qui permet au développeur d’interagir avec le gestionnaire des fichiers du système d’exploitation et par conséquent de pouvoir utiliser des méthodes pour créer, renommer, effacer, copier, etc. des fichiers.
Les événements de l’utilisateur vs. les événements du système
Une similitude s’applique également à l’approche orientée aux événements de JavaScript côté-client qui est présente dans Node.js. Dans une approche évènementielle, du code est exécuté lorsqu’un certain événement s’avère. La différence entre côté-client et Node.js consiste dans le fait que pour JavaScript côté-client les événements se réfèrent principalement à des actions de l’utilisateur confinées à chaque séance individuelle d’un utilisateur sur la page. Au contraire, avec Node.js, les événements acquièrent une portée globale et se réfèrent aux événements liés à l’ensemble de l’application Node.js et son interaction avec le système hôte et ses composantes. Node.js utilise un gestionnaire d’événements qu’on appelle le « event loop » et qui est représenté dans le diagramme suivant :
Un exemple peut contribuer à éclaircir ce passage : lorsque le développeur demande à Node.js de lire le contenu d’un fichier, il obtiendra un événement qui détermine quand la lecture a été menée à bien. Ce simple exemple permet également d’introduire des aspects très importants de Node.js :
- Le principe du non-blocking
- Les fonctions de callback
Non-blocking I/O et callback
Pour bien comprendre ce passage il faut avoir à l’esprit que tout mécanisme d’I/O requière une certaine puissance de computation, c’est-à-dire du temps pour exécuter des opérations soit symboliques (e.g., calculer le résultat d’un algorithme), soit physique (e.g. écrire du texte dans un fichier et le sauvegarder). Dans la plupart des langages de programmation, ces opérations d’Input/Output sont exécutées lorsque l’interprète les « rencontre » dans le code source, et avant de poursuivre dans la lecture du code, l’interprète attend que l’opération se termine. En d’autres termes, l’opération bloque la poursuite de l’exécution du code. Ce type de mécanisme, qui s’applique notamment à JavaScript côté-client, a l’avantage que les opérations se font en séquence, et donc ce qui est disponible à la ligne de code 1 sera disponible à la ligne de code 2. Voici un exemple côté-client qui montre ce principe
//Code JavaScript côté-client !! var monNom = prompt(‘What is your name ?’); console.log(monNom);
Dans JavaScript côté-client, la méthode prompt
de l’objet global window bloque l’exécution du script jusqu’à ce que l’utilisateur n’ait pas fourni une réponse dans la boîte qui s’affiche dans le navigateur, ce qui donc met les autres lignes du code en attente.
Pour éviter ce type de bloque, Node.js exploite à fond les événements asynchrones en associant ce qu’on appelle une fonction de callback à des opérations qui tendent à prendre du temps et donc à bloquer potentiellement la lecture du code. Une fonction de callback est une fonction qui est appelée lorsqu’une opération est terminée et qui reçoit automatiquement le résultat de la première opération en tant qu’argument. Voici un exemple fictif de fonction de callback qui affiche le contenu d’un fichier une fois terminée la lecture (pour un exemple réel de ce mécanisme voir le module fs
plus bas dans la page) :
//Code illustratif, pas exemple réel !! lireLeFichier(‘fichier.txt’, function (contenu) { //faire quelque chose avec le contenu }
Qu’est-ce qu’un module Node.js
Les « building blocks » de Node.js sont les modules, c’est-à-dire des extensions – sous forme d’objet – qui permettent d’accéder à des propriétés et des méthodes en lien avec le système hôte (i.e. l’environnement) et à d’autres éléments qui peuvent être utile dans le cadre d’une application (e.g. base de données, client email, etc.). Contrairement à l’objet global window dans JavaScript côté-client, les différents modules de Node.js ne sont pas toujours disponibles dans l’application : ils seraient trop et trop lourdes à charger dans chaque application. De ce fait, le programmateur doit identifier à l’avance les modules dont il aura besoin pour son application et les importer dans son application.
Anatomie d’un module
Un module est l’équivalent d’un objet personnalisé en JavaScript, mais avec la particularité que le programmateur peut décider de rendre seulement certaines méthodes et propriétés de cet objet disponibles dans d’autres scripts. Pour ce faire, on utilise l’objet exports
qui détermine les méthodes et propriétés « publiques » du module. Voici un exemple d’un module inclus dans le fichier stic.js
:
//Fichier stic.js qui représente un module
//Variables qui ne sont pas disponibles avec le module
var name = "STIC I";
var subjects = ['HTML5', 'Programmation', 'JavaScript', 'Interactivité'];
//Méthode addSubject disponible avec le module
exports.addSubject = function (subject) {
subjects.push(subject);
}
//Méthode about disponible avec le module
exports.about = function () {
return name + " est un cours du Master MALTT qui porte sur " + subjects.join(", ");
}
Ce script n’est pas censé faire quelque chose. Au contraire, son utilité et d’être inclus dans un autre script qui puisse se servir des méthodes publiques addSubject()
et about()
. Pour se faire, il faut inclure le module dans le fichier du script grâce à la fonction require()
. Voici un exemple de script contenu dans un fichier script.js qui se trouve dans la même position du fichier stic.js :
//Fichier script.js qui représente le script à exécuter
var stic = require('./stic.js');
console.log(stic.about());
stic.addSubject('Node.js');
console.log(stic.about());
Le résultat de node script.js
à la console est le suivant :
STIC I est un cours du Master MALTT qui porte sur HTML5, Programmation, JavaScript, Interactivité STIC I est un cours du Master MALTT qui porte sur HTML5, Programmation, JavaScript, Interactivité, Node.js
La fonction require
On peut utiliser les modules en les important grâce à la fonction require()
. Cette fonction accepte un argument qui peut être :
- Le path à un fichier JavaScript, avec ou sans .js final -> require(‘lib/mesmodules/stic-1’) ;
- Le nom d’un module natif de Node.js (voir plus bas dans la page) -> require(‘fs’) ;
- Le nom d’un module installé avec npm (de manière global ou dans le dossier node_modules) -> require(‘mysql’) ;
Normalement, on associe la fonction require()
à una variable ainsi que les propriétés et les méthodes exportées peuvent être utilisées grâce à la notation par point :
var myModule = require(‘./myModule.js’); myModule.myProperty; myModule.myMethod();
Tableau comparatif
Côté client | Node.js | |
---|---|---|
Langage utilisé | JavaScript | JavaScript |
Environnement | Navigateur web | Système hôte (ordinateur, server, dispositif, ...) |
Disponibilité | Automatique dans le navigateur | Installation nécessaire |
Version disponible | Dépend du navigateur de l'utilisateur | Dépend de la version installée sur le système |
Accès à... | Certaines fonctionnalités du navigateur | Opérations de système |
Accès grâce à... | Objet global window | Modules natifs, à installer ou personnalisés |
Opérations I/O | Limitées au navigateur | ~ Illimitées dans le système |
Événements | Plutôt liés aux actions de l'utilisateur | Plutôt liés aux opérations de système |
Exécution du code | Prioritairement synchrone (blocking) | Prioritairement asynchrone (non-blocking) |
Les modules natifs de Node.js
Dans cette section nous présentons un aperçu d’une sélection de modules disponibles « out of the box » avec l’installation de Node.js sur un système. Pour une liste plus exhaustive se référer à la documentation de l’API sur le site officiel.
fs : le FileSystem
Le module fs
(voir documentation API) permet d’exécuter les opérations les plus communes sur des fichiers et des dossiers telles que créer, modifier, effacer, changer les permissions, obtenir des informations sur la taille, la dernière modification, etc.
Pour chaque opération il existe normalement deux méthodes, une méthode synchrone (blocking) et une méthode asynchrone (non-blocking) avec fonction de callback (voir plus haut dans la page). Voici de suite quelques simples exemples qui utilise le module fs
.
Lire le contenu d’un fichier
Pour lire le contenu d’un fichier on peut utiliser les méthodes :
readFile(file, options, callback)
pour une lecture asynchronereadFileSync(file, options)
pour une lecture synchrone
Les deux fonctions sont similaires à l’exception de la présence de la fonction de callback pour la lecture asynchrone. Les arguments communs des deux fonctions sont :
- Le fichier à lire, avec sa position absolue ou relative à la position du script qui invoque la méthode
- Des options dont la plus utile concerne l’encodage du résultat de la fonction qui permet d’obtenir le contenu du fichier en tant que String (suite de caractères)
La fonction asynchrone nécessite ensuite d’une fonction de callback qui reçoit les deux arguments suivants :
- Error : si la lecture n’est pas possibile, le système propose le type d’erreur rencontré
- Data : le contenu du fichier
Voici un exemple de lecture asynchrone d’un fichier file.txt
qui contient tout simplement la phrase Texte dans le fichier!
:
//Importer le module FileSystem
fs = require('fs');
//Lire le contenu du fichier file.txt de manière asynchrone
//Afficher ensuite son contenu en tant que suite de caractères utf8
fs.readFile('file.txt', 'utf8', function (error, data) {
//Contrôler qu'il n'y ait pas d'erreur
if(error) {
console.log("Erreur dans la lecture du fichier");
return;
}
console.log(data);
})
//Afficher un message pour montrer l'ordre des opération
console.log("Texte dans la console!");
Le résultat à la console de ce script est le suivant :
Texte dans la console! Texte dans le fichier!
On peut noter que c’est le message « Texte dans la console ! » s’affiche avant le contenu du fichier car on a utilisé une fonction non-blocking, par conséquent l’interprète a continué à lire les instructions pendant qu’il lisait le contenu du fichier file.txt. Voici le même exemple mais en utilisant la fonction synchrone :
//Importer le module FileSystem
fs = require('fs');
//Lire le contenu du fichier file.txt de manière synchrone
//Afficher le contenu
var content = fs.readFileSync('file.txt', 'utf8');
console.log(content);
//Afficher un message pour montrer l'ordre des opérations
console.log("Texte dans la console!");
Le résultat à la console de ce script sera par contre le suivant :
Texte dans le fichier! Texte dans la console!
On peut noter que, contrairement à l’exemple asynchrone précédent, dans ce cas l’ordre des messages respecte l’ordre du code dans le fichier, car nous avons utilisé une fonction synchrone qui a bloqué l’exécution du code pendant la lecture du contenu du fichier file.txt.
Écrire le contenu d’un fichier
Pour écrire le contenu d’un fichier on peut utiliser la fonction writeFile()
ou sa version synchrone writeFileSync()
. Le comportement de ces fonctions est similaire aux fonctions readFile()
et readFileSync()
illustrées au point précédent, mais avec l’opération d’écriture. Les deux fonctions écrivent le contenu dans un fichier passé en argument, si le fichier existe déjà, le contenu de ce fichier sera remplacé. Si le fichier n’existe pas encore, il sera créé automatiquement.
La fonction writeFile()
accepte les arguments suivants :
- Le nom du fichier (avec éventuellement sa position absolue ou relative au script) dans lequel placer le contenu
- Le contenu du fichier qui peut être tout simplement une suite de caractères (string)
- Des options (facultatives) dont l’encodage qui est fixé à utf8 si pas spécifié
- La fonction de callback
fs.writeFile(fichier, contenu, [options], callback) ;
Voici un exemple – tiré de la documentation officielle de Node.js (veuillez noter qu'il faut importer le module fs pour que cet exemple puisse marcher, i.e. var fs = require('fs')
) – qui écrit une simple phrase dans un fichier message.txt
de manière asynchrone :
fs.writeFile('message.txt', 'Hello Node.js', function (err) {
if (err) throw err;
console.log('It\'s saved!');
});
Si vous voulez ajouter le contenu à un fichier plutôt que remplacer son contenu, vous pouvez utiliser alors la fonction appendFile()
(si le fichier n'existe pas, il sera créé automatiquement). Voici un exemple tiré encore une fois de la documentation officielle de Node :
fs.appendFile('message.txt', 'data to append', function (err) {
if (err) throw err;
console.log('The "data to append" was appended to file!');
});
Lister les fichiers présents dans un dossier
Le module fs
permet également d'agir sur les dossiers. Par exemple la fonction fs.readdir()
(disponible également en modalité synchrone) permet de lister les fichiers contenus dans un dossier. Voici d'abord l'arborescence pour comprendre les références aux chemins de fichiers :
directory |- file1.txt |- file2.txt |- file3.txt script.js
Le fichier script.js à exécuter avec node se trouve donc dans la même position du dossier dont on veut lister le contenu. Voici le code pour le faire :
//Importer le module du File System
var fs = require('fs');
//Lire le contenu d'un dossier appelé "directory" dans le même chemin du script
//La fonction asynchrone returne une erreur, si présente, ou un array des fichiers
fs.readdir('./directory/', function (error, files) {
//Afficher l'erreur
if(error) throw error;
//Itération dans l'array files pour obtenir le nom du fichier
for(var i = 0; i < files.length; i++) {
console.log(files[i]);
}
});
Bibliographie
- Wilson, J. R. (2013). Node.js the Right Way. The Pragmatic Bookshelf.