Le temps où Node.js régnait en maître comme la solution incontournable pour exécuter du code JavaScript côté serveur est-il révolu ? En tout cas, il a aujourd’hui des challengers de taille comme Bun (qui pourrait lui aussi mériter une dépêche) ou Deno. C'est donc de ce dernier qu'il sera question dans cette dépêche, à l'occasion de la sortie de sa version 2.0
Sommaire
Pour rappel
Deno est un runtime JavaScript et TypeScript. Il a vu le jour suite au constat de Ryan Dahl (créateur aussi de Node.js), que Node avait des problèmes de conceptions, et qu'il était nécessaire de repartir de zéro en tenant compte de l'expérience de Node pour ne pas refaire les mêmes erreurs. Il imagine Deno comme un runtime avec un modèle de sécurité par défaut plus strict. Les programmes Deno n'ont pas accès au système de fichiers, au réseau ou à l'environnement, sauf si on leur accorde explicitement ces permissions. Deno est écrit en Rust, et se base sur le moteur JavaScript V8 de Google. Deno se distingue également de Node en offrant la possibilité d'importer les dépendances via des URL, mettant en cache chaque module lors de l’importation pour améliorer la vitesse d’exécution.
La mascotte !
La première chose notable quand on passe de Node.js à Deno, c'est sa mascotte ! En effet, même si Node.js possède bien une petite tortue comme mascotte, celle-ci n'est utilisée nulle part ! Personnellement, j'ai toujours trouvé bien plus chouettes les projets qui ont des petites bestioles comme mascotte (Mozilla, Tux …). Et chez Deno, le dinosaure mascotte est omniprésent sur tout le site. Et en plus, à l'occasion de la version 2.0, on peut habiller notre dino sur la home page du projet ! Et ça c'est cool ! Voici le mien, qui est en compagnie de Ferris, la mascotte officieuse de Rust !
Bon, comme je ne suis pas sûr que tout le monde partage ma passion pour les mascottes, on va passer au côté plus technique ! 🤣
Deno 1.x, des débuts difficiles !
La version 1.0 sortie en mai 2020 a du mal à se faire une place et reste dans l'ombre de son grand frère. En effet, même si Deno offre un grand lot de nouveautés et est plus sécurisé par défaut, la très large adoption de Node et le fait que les projets développés pour Node ne sont pas forcément compatibles avec Deno rend l’adoption de ce dernier difficile. De plus, l'utilisation de CDN plutôt que d'installer les dépendances localement (dans le répertoire node_modules
) a certes de nombreux avantages, mais cela rend votre projet dépendant de disponibilité du réseau ou peut entraîner des problèmes de performances si le CDN est éloigné géographiquement.
Les nouveautés de la version 2.0
Deno est désormais 100% compatible avec Node.js, et un gestionnaire de paquets officiel a vu le jour. Vous pouvez maintenant utiliser deno add
et deno remove
pour ajouter ou retirer un paquet à votre projet.
Autour du projet Deno, JavaScript Registry (JSR) un dépôt de paquets JavaScript universel !
Le registre NPM s'est construit autour de Node.js afin de gérer facilement les dépendances de nos projets. Il a donc été développé pour Node.js à une époque où Node était la seule solution pour exécuter du code JavaScript côté serveur. En près de 15 ans, le registre NPM a rassemblé un peu moins de 3 millions de paquets et a très largement rempli sa mission toutes ces années. Mais aujourd'hui, la situation a changé, il existe plusieurs runtimes pouvant exécuter du code JavaScript (ou TypeScript) côté serveur. Et du côté front-end, les frameworks se sont multipliés et sont devenus de plus en plus complexes et nécessitent aussi l'utilisation d'un gestionnaire de paquets. Un registre de paquets fondé autour de Node.js uniquement est donc beaucoup moins pertinent qu'en 2010.
C'est donc pourquoi, à l'initiative du projet Deno, un nouveau registre de paquets JavaScript et TypeScript universel pointe aujourd'hui le bout de son nez. Il s'agit donc de JSR (JavaScript Registry).
Dans JSR, quand on va sur la page d'un paquet, en haut à droite, on a les logos des environnements compatibles avec le paquet :
Performances du runtime
Niveau performance, ça donne quoi ?
On voit souvent l'affirmation que Deno serait plus rapide que Node.js. Mais ça donne quoi en réalité ?
J'ai voulu faire un petit test sans prétentions pour voir ce que ça donne. Je voulais faire des tests plus poussés sur différents systèmes d'exploitation et architectures, mais par manque de temps, le test sera donc fait sur un seul système et un seul ordinateur et il s'agit d'un Mac… Un comble pour LinuxFr.org, mais c'est l'ordinateur que j'avais à disposition à ce moment-là. Mais sinon, je ne porte pas spécialement Apple dans mon cœur, bien au contraire !
J'ai testé l’exécution d'une même API sur Node. et Deno pour voir les différences de performance entre ces solutions. Pour ce test, j'ai utilisé une API Rest que j'ai développée pour le site de la société AudioSoft. J'ai fait la même requête POST 10 fois sur la même route avec les mêmes données. Il est important de préciser que c'est la première fois que je fais ce genre de tests, et que je ne fais peut-être pas tout dans les règles de l'art. Il y a des éléments extérieurs à Node et Deno qui peuvent influencer les scores. Notamment, la base de données utilisée pour le test était accessible via Internet, et des différences de débit ont pu fausser les tests.
Test sur un MacBook Pro (2,6 GHz Intel Core i7 6 cœurs, AMD Radeon Pro 5300M 4 Go Intel UHD Graphics 630 1536 Mo, 16 Go 2667 MHz DDR4) sous macOS Sonoma
Node: Le temps moyen pour exécuter le test de 126 millisecondes
Deno: Le temps moyen pour exécuter le test de 93 millisecondes
Performances du gestionnaire de paquets
Comme dit précédemment, Deno c'est aussi un gestionnaire de paquets. J'ai donc trouvé intéressant de tester les principaux gestionnaires de paquets sur différents environnements.
Pour ce test je me base sur la même API Rest que pour le test précédant, les dépendances à installer pour cette API sont : bcrypt, body-parser, dotenv, express, jsonwebtoken, mariadb, multer, mysql2, nodemailer, et sequelize. Le test a été fait sur un MacBook Pro. Pour effectuer ce test, le cache des gestionnaires de paquets ont été nettoyés et les fichiers-verrous supprimés.
Avec NPM, l'installation a mis 10 secondes.
Avec Deno, l'installation a mis 1 seconde.
Avec Bun, l'installation a mis 3 secondes.
On voit très clairement que NPM est beaucoup plus lent que ses deux concurrents. L'écart est plus faible entre Deno et Bun. Mais Deno est bien le plus rapide des trois.
Avant de réaliser ce test, j'en ai effectué un en oubliant de nettoyer le cache et de supprimer package-lock.json. Les résultats étaient alors 8 secondes pour NPM, 5 secondes pour Deno et 4 secondes pour Bun. Il est logique de constater que NPM est plus rapide, en revanche, je trouve surprenant que Deno et Bun aient été ralentis. Il est possible que les gestionnaires de paquets aient parcouru package-lock.json pour garder les versions présentes dans ce fichier, ce qui les aurait tous les trois ralentis. Et NPM a peut-être pu bénéficier de son cache (car je l'utilise bien plus que les deux autres sur mon ordinateur), Deno et Bun eux n'avaient peut-être pas grand-chose dans leurs caches, ont donc été ralentis. Il est donc important de supprimer les lockfile en cas de migration d'un projet.
Comme je le disais plus haut, c'est la première fois que j'effectue ce genre de test comparatif. Si vous avez des conseils sur les bonnes méthodes pour faire des tests plus fiables, ça m’intéresse !
Deno 2.1 est là
Étant donné que j'ai mis environ un siècle pour rédiger cette dépêche, Deno 2.1 est sortie entre temps ! 🤣
Je vous liste donc les principales nouveautés apportées à la version 2.1 sans les commenter 😉
- Support natif de WebAssembly (Wasm) : Il est désormais possible d'importer directement des modules Wasm, simplifiant leur utilisation et améliorant les performances.
- Version Long Term Support (LTS) : Deno 2.1 inaugure la première version LTS, garantissant des correctifs de bugs et des améliorations de performance pendant… Six mois… On n'est pas encore aux 30 mois des versions LTS de Node.js… Cela viendra peut-être plus tard. 🙂
- Commande deno init --npm vite : Cette commande simplifie la création de nouveaux projets en utilisant des outils comme Vite, en automatisant l'initialisation et en réduisant la configuration manuelle.
- Gestion des dépendances : Introduction de la commande deno outdated pour gérer les mises à jour des dépendances JSR et npm.
Conclusion
Si vous êtes développeur Node.js, je vous conseille de vous intéresser à Deno, et même à Bun. Je ne sais pas si ces deux runtime sont totalement prêts pour des projets en production (par exemple, Deno 2.1 n'a que 6 mois de durée de vie, ce qui est plutôt contraignant pour les serveurs.). Mais peut-être que dans un futur proche, il sera cohérent de migrer vers l'un de ces deux-là.
Aller plus loin
- Annonce sur le blog officiel (58 clics)
- Site officiel de Deno (131 clics)
- JSR (41 clics)
# JSR
Posté par Julien Jorge (site web personnel) . Évalué à 10 (+19/-0).
Merci pour cette très intéressante dépêche :)
Dans l'idée de repartir de zéro et ne pas refaire les mêmes erreurs, quels sont les moyens mis en œuvre au niveau JSR pour éviter les problèmes récurrents autour de NPM qui ont alimenté les news ces dernières années ? Je pense à des problèmes pratiques du genre ça télécharge la terre entière, ou encore le fait que tout s'écroule quand un paquet disparaît ; mais aussi aux questions de sécurités liées au typosquatting et à l'introduction de modules malveillants planqués dans de multiples couches de dépendances.
[^] # Re: JSR
Posté par potate . Évalué à 10 (+10/-0).
Pour ce problème là en particulier je ne pense pas qu'une solution technique apportée par le gestionnaire de paquets soit possible : ce sont les devs qui sont responsables du nombre de dépendances et de leur taille.
Je voulais un exemple donc j'ai été chopper une dépendance transitive triviale dans le yarn.lock du projet angular-cli :
is-string
.Personnellement je me questionne sur l'utilité d'une dépendance dont le but est de vérifier si une variable contient une chaine de caractère (certes celle-ci comprend plus qu'un simple
const isString = (s) => typeof s === 'string'
, mais les 1490 bibliothèques publiées sur npm qui dépendent de celle-ci ont-elles vraiment besoin de plus ?).J'ai aussi balancé un
npm install is-string
dans un dossier, ce qui m'a permis de voir que le dossiernode_modules
contenait alors 14 bibliothèques (soit 'is-string' + 13 dépendances directes et transitives) pour un poids total de 590 Ko.Et toujours sur la page de npmjs je vois que cette bibliothèque est téléchargée 32 millions de fois par semaine.
Bref, le gaspillage me parait énorme, mais dans ce cas plutôt causé par les pratiques que par les outils.
[^] # Re: JSR
Posté par xryl669 . Évalué à 1 (+0/-0).
Parce que:
assert.ok(isString(Object('foo')));
Après, est-ce utile?, tout est là.
# je reste septique sur un point ...
Posté par totof2000 . Évalué à 2 (+0/-0).
Le fait de ne pas donner accès au réseau ou au filesystem … Je suis peut-être à côté de la plaque mais de mon point de vue, ce n'est pas au runtime de s'occuper de ça : aujourd'hui il y a suffisamment de solutions talles que les conteneurs par exemple our assurer le cloisonnement d'un process quelconque. Le risque est à mon avis, de multiplier les couches et surcouches d'isolation et de se retrouver avec des galères pas possibles pour gérer les accès de ces diverses couches. D'autant plus qu'un process côté serveur qui ne doit accéder ni au filesystem, ni au réseau ne doit pas être utile à grand chose.
[^] # Re: je reste septique sur un point ...
Posté par barmic 🦦 . Évalué à 4 (+2/-0).
Je me sers justement de deno pour ça :)
Ma boite donne une fonctionnalité qui permet grosso modo à des utilisateurs d'écrire une fonction js que l'on exécute en suite nous sur nos serveurs. C'est une forme de plugin.
Et ben pour ça on a un service qui spawn un deno pour exécuter cette fonction (et quelques autres trucs) et communique avec via
std{in,out,err}
. C'est une forme de séparation de privilège comme le décrit le projet openbsd où ils découpent les logiciels entre la partie qui a besoin de privilège et celle qui n'en a pas.A noter aussi qu'il est possible d'être plus fin et par exemple de limiter l'accès uniquement à une partie du système de fichier.
Alors que je me suis jamais vraiment penché sur node, j'aime vraiment bien deno qui est devenu l'un des interpréteurs que j'utilise pour différents scripts juste pour moi en remplacement de python. J'ai des scripts qui ne font plus ou moins que des appels rest, typescript est très bon pour ça sans la moindre dépendance en plus (bon deno du coup). Et pour les fois où je fais du scrapping fetch + domparser est plus pratique pour moi que request + beautifulsoup.
Une fonctionnalité que je trouve vraiment sympa, c'est que tu peut avoir de la persistance simple sans prise de tête avec deno puisqu'il implémente le localstorage. Ça permet à certains de mes scripts d'avoir un cache sans m'embêter plus que ça.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: je reste septique sur un point ...
Posté par totof2000 . Évalué à 2 (+0/-0).
Ce que tu décris me fait penser un peu aux cloud functions de GCP ou lambda AWS, mais …
Quel genre de fonctions ? J'ai du mal à me représenter les cas d'utilisation. Tu parle d'interaction avec stdin/stdout/stderr, mais ces 3 canaux doivent bien être interfacés avec des entréess ou des sorties non ? Je suis assez curieux car ça pourrait me servir en début d'année prochaine …
[^] # Re: je reste septique sur un point ...
Posté par barmic 🦦 . Évalué à 3 (+1/-0).
Tout à fait et faire du FAAS aurait put être une option, mais disons que faire un choix de déploiement et un choix de dev n'est pas les même personnes ni les même enjeux.
Interaction est un bien grand mod pour être tout à fait précis :
Le code qui reçoit la requette va :
../file.txt
par exemple)Le processus lit sa conf, regarde la requête dans son stdin et fait son job.
Si tous se passe bien il écrit le résultat dans sa sortie standard, sinon il écrit l'erreur dans sa sortie d'erreur.
Le parent pose un timer pour limiter le temps d’exécution du sous processus.
Du coup c'est juste un bête request/response, ça aurait pu être fait en HTTP sur l'interface loopback. Je sais pas si deno sait utiliser une socket UNIX.
Je sais pas si c'est suffisamment clair.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: je reste septique sur un point ...
Posté par totof2000 . Évalué à 3 (+1/-0). Dernière modification le 17 décembre 2024 à 16:45.
En fait ce que je ne comprend pas, c'est ceci :
Il doit bien accéder au FS pour créer un dossier non ? Ou quelque chose m'échappe …
En fait ce que j'ai du mal à m'imaginer, c'est comment le process s'interface avec le monde extéreur. Mais si tu as un lien vers un exemple je suis preneur.
[^] # Re: je reste septique sur un point ...
Posté par barmic 🦦 . Évalué à 2 (+0/-0). Dernière modification le 17 décembre 2024 à 17:02.
Ce n'est pas lui qui crée le dossier, je lui dis quel dossier il a le droit de lire et m'assure qu'il n'a le droit de lire que ce dossier
Le premier pour qu'il ai le droit de lire ce dossier et le second pour que le script puisse savoir quel dossier il peut lire.
Tu as la doc ici:
https://docs.deno.com/runtime/fundamentals/security/#file-system-access
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: je reste septique sur un point ...
Posté par totof2000 . Évalué à 2 (+0/-0).
Merci je regarde ça. Cela dit, j'ai quand même l'impression que ça fait un peu double-emploi avec l'isolation apportée par un conteneur. En fait en reformulant, j'essaie de comprendre l'intérêt de l'un par rapport à l'autre.
[^] # Re: je reste septique sur un point ...
Posté par barmic 🦦 . Évalué à 2 (+0/-0).
En fait tu peu le faire avec des containers, mais il faut instancier un container par utilisateur différent (c'est pour ça que le faas a du sens).
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
[^] # Re: je reste septique sur un point ...
Posté par totof2000 . Évalué à 2 (+0/-0).
Ok, je comprends mieux. Merci pour les explications.
[^] # Re: je reste septique sur un point ...
Posté par totof2000 . Évalué à 3 (+1/-0).
Si j'en crois le lien que tu as donné plus bas :
[^] # Re: je reste septique sur un point ...
Posté par barmic 🦦 . Évalué à 2 (+0/-0).
bien vu
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
Envoyer un commentaire
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.