Développement Web

L’environnement de développement Node.js

Romuald THION

Semestre pair 2022 UNC

Node.js

Node.js

Node.js versus browser

Serveur/back

  • moteur V8 uniquement
  • accès système complet
    • sockets, FS, threads…
  • pool de threads libuv
    • API callbacks
    • API Promise
  • modules
    • CommonJS (CJS)
    • EcmaScript (ESM)

Client/front

  • moteur du browser
  • limitations de sécurité
    • CORS, localStorage…
  • gestion async du browser
    • API Promise
  • modules EcmaScript

Node.js s’aligne progressivement sur le standard ES, mais les APIs historiques restent.

Exécution de programme Node.js

S’utilise, comme Python :

  • soit en interpréteur intéractif REPL (Read Eval Print Loop)
  • soit en interpréteur non-intéractif avec node file.js

A la différence de Python:

  • Node.js intègre nativement une boucle d’événements
    • programmation est essentiellement asynchrone/événementielle
    • applications orientées système et réseaux, backend
  • les applications I/O intensive sont performants

La boucle d’événements Node.js

Source - RichOnTheWeb

Event-driven architecture

https://nodejs.org/api/events.html#events

Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called “emitters”) emit named events that cause Function objects (“listeners”) to be called.

Toute l’architecture Node.js est asynchrone, orientée événements :

  • permet de gérer la concurrence
  • est performant
    • les applications Node.js sont I/O intensive, rarement CPU intensive
    • en particuleir pour la cas du Web
  • applicable à toute l’API Node.js
    • fichier, stream, socket, crypto, process

Exemple ping/pong

Fichier events.mjs

import { EventEmitter } from "node:events";
import { setTimeout } from "node:timers/promises";

const emitter1 = new EventEmitter();
emitter1.last = Date.now();

emitter1.on("ping", async function pingListener(value, time) {
  console.info(`[1] received ${value}@+${time - emitter1.last}`);
  emitter1.last = time;
  await setTimeout(Math.random() * 1000);
  emitter2.emit("ping", value + 1, Date.now());
});

const emitter2 = new EventEmitter(); // [...] idem emitter1
emitter1.emit("ping", 0, Date.now());
// [1] received 0@+1
// [2] received 1@+950
// [1] received 2@+1040
// [2] received 3@+968

Performance des programmes I/O intensive

Exemple sur le réducteur d’URL GET (TP6)

> siege --benchmark --concurrent=100 --time=30s --header="Accept:application/json" 'http://127.0.0.1:8080/api-v1/'
** SIEGE 4.0.4
** Preparing 100 concurrent users for battle.
Transactions:             83395 hits
Availability:            100.00 %
Elapsed time:             29.19 secs
Data transferred:          1.27 MB
Response time:             0.03 secs
Transaction rate:       2856.97 trans/sec
Throughput:                0.04 MB/sec
Concurrency:              99.74
Successful transactions:  83395
Failed transactions:          0
Longest transaction:       0.12
Shortest transaction:      0.02

Exemple sur le réducteur d’URL POST (TP6)

> siege --benchmark --concurrent=100 --time=30s --content-type="application/json" 'http://127.0.0.1:8080/api-v1/ POST {"url":"http://perdu.com"}'
** SIEGE 4.0.4
** Preparing 100 concurrent users for battle.
Transactions:             45210 hits
Availability:            100.00 %
Elapsed time:             29.04 secs
Data transferred:          6.42 MB
Response time:             0.06 secs
Transaction rate:       1556.82 trans/sec
Throughput:                0.22 MB/sec
Concurrency:              99.71
Successful transactions:  45210
Failed transactions:          1
Longest transaction:       0.25
Shortest transaction:      0.03

L’environnement de développement

NPM

npm

npm est le Node Packet Manager.

npm est à Node.js ce que pip est à Python, avec en plus un support natif des environnements virtuels à la venv .

npm --version
# 8.16.0

npm init
# question interactives
npm install slugify
# added 1 package in 2s

cat main.js
# import slugify from "slugify"
# console.log(slugify("C'est un test ♥ !"));

node main.js
# C'est-un-test-love-!

le fichier package.json

{
  "name": "cm4_exemples",
  "version": "1.0.0",
  "description": "Un exemple de serveur HTTP de base",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "DEBUG=app nodemon server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "debug": "^4.3.2",
    "dotenv": "^10.0.0"
  },
  "devDependencies": {
    "eslint": "^7.32.0",
    "nodemon": "^2.0.12"
  }
}

La définition du projet et ses dépendences, voir docs.npmjs.com

Exemple: lancement de package

JSON = JavaScript Object Notation : une représentation textuelle (une serialization) des objets JavaScript

Avec le fichier package.json précédent :

npm install
# added 296 packages in 2s

npm run dev
# > cm4_exemples@1.0.0 dev
# > cross-env DEBUG=app nodemon server.mjs

# [nodemon] 2.0.19
# [nodemon] to restart at any time, enter `rs`
# [nodemon] watching path(s): *.*
# [nodemon] watching extensions: js,mjs,json
# [nodemon] starting `node server.mjs`
#   app Server listening at http://127.0.0.1:5000/ +0ms

npx the package runner

npx permet d’exécuter des commandes locales au dossier (dans node_modules/) sans les installer globalement

https://www.npmjs.com/package/npx

Executes command either from a local node_modules/.bin, or from a central cache, installing any packages needed in order for command to run.

npx est implicitement utilisé par les scripts du package.json.

Exemple

/tmp/npx❯ npx cowsay "Hello world"
Need to install the following packages:
  cowsay@1.5.0
Ok to proceed? (y) y
 _____________
< Hello world >
 -------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
/tmp/npx❯ ll
/tmp/npx❯

Les sytèmes de modules

Plusieurs systèmes d’import de bibliothèques co-existent dans Node.js

  • Common JS (CJS) : le système historique
    • const maLib = require('laLib');
    • module.exports = { ... };
    • par défaut pour les extensions de fichier .js
  • EcmaScript (ESM) : le système standard JS
    • import maLib from 'laLib';
    • export default { ... };
    • par défaut pour les extensions de fichier .mjs

Préférer les modules ESM et l’extension .mjs

TODO

Exemple démo avec le premier serveur Web

On donner l’exemple d’un serveur web server.mjs avec le projet package.json.

  1. télécharger les 2 fichiers dans un dossier de travail
  2. changer le port pour le 5000 dans le package.json
  3. installer les dépendances avec npm install
  4. exécuter le script npm run dev
  5. se connecter à l’application http://127.0.0.1:5000/

TP5

Faire la partie 1 du TP5