Deno : un successeur Ă nodeJS ?
Lecture 9 min. âą
Table des matiĂšres

Je dois vous avouer que jâai pris trĂšs rĂ©cemment connaissance de ce projet.
Depuis quâil est sorti en version 1.0.0 le 13 mai dernier, il a commencĂ© Ă faire du bruit sur twitter et a fini par arriver Ă mes oreilles.
Je ne fais pas plus de nodeJS que ça mais jâai rĂ©cemment commencer Ă me mettre sur un projet en typescript, donc quand jâai lu que deno pouvait comprendre de maniĂšre native le typescript. Je me suis que ça serait bien dây jeter un petit coup dâoeil. Ce qui est chose faite đ.
Le projet se prononce âDinoâ pour faire le jeu de mot. Un bon nom de projet doit toujours avoir un jeu de mots đ€.
Cet article est un peu tout Ă la fois: un pense-bĂȘte, un bilan de ce que jâai pu comprendre de deno et un espace dâexpĂ©rimentation.
Caractéristiques
Déjà , que promet Deno?
Deno nâest pas un langage de programmation mais un runtime. Il permet de maniĂšre native de faire tourner Ă la fois du typescript et du javascript.
Attention! Deno nâest pas du nodeJS amĂ©liorĂ©, câest une réécriture totale en Rust â€ïžđŠ et utilise un nouvelle API de binding du moteur V8 sous la forme dâune lib appelĂ©e rusty_v8. Ceci implique que les modules de nodeJS comme http nâexiste pas en tout cas pas sous la mĂȘme forme que sous nodeJS.
Sa principale promesse est dâoffrir un runtime sĂ©curisĂ© Ă la fois pour lâexĂ©cution du javascript mais aussi du typescript.
Une autre promesse est de sâaffranchir des node_modules. En tĂ©lĂ©chageant une fois pour toute les dĂ©pendances pour les stocker dans un endroit unique sur le disque dur de lâutilisateur.
Deno permet aussi dâutiliser les API standards propres aux navigateurs internet modernes que sont fetch et lâobjet window.
Il comporte aussi (pour le moment de maniĂšre instable âĄïž) des fonctionnalitĂ© comme les WebWorkers. Permettant de rĂ©aliser des opĂ©ration en tĂąche de fond grĂące Ă lâutilisation de threads sĂ©parĂ©s. Ceci Ă©tant permis grĂące Ă lâarchitecture du projet se basant sur la cĂ©lĂšbre et robuste lib rust tokio.

Il existe encore bien dâautre chose possible Ă rĂ©aliser mais pour le moment installons le Dino ^^.
Installation
JâĂ©cris ces lignes depuis un MacOS, je vais donc installer deno via homebrew mais il existe un nombre assez invraisemblable dâinstaller dino. Je vous laisse faire votre marchĂ© en fonction de votre OS et de votre prĂ©fĂ©rence.
De mon cÎté, je vais donc éxécuter :
brew install dino
Si tout sâest bien passĂ© vous devriez pouvoir faire :
# deno --version
deno 1.0.0
v8 8.4.300
typescript 3.9.2Parfait câest installĂ© đ!
Mes premiers programmes
Deno est fait pour faire tourner à la fois du js et du ts, comme je préfÚre les langage typé. Allons y pour du ts.
Hello World
Je créé mon fichier hello.ts
// hello.ts
console.log("Hello from đŠ")Que je lance via:
# deno run hello.ts
Compile file:///lab/deno/playground/hello.ts
Hello from đŠVous pouvez remarquer quâil existe une phase de compilation, celle ci disparaĂźt si vous relancez le deno run sans changer la source.
Le site de deno.land propose tout un tas dâĂ©tudes de cas et dâexemple de code. Nous allons en Ă©tudier certains.
Manipuler des fichiers
Il sâagit dâune réécriture de la commande UNIX cat qui a pour but de concatĂ©ner le contenu dâun ensemble de fichiers.
// cat.ts
for (let i = 0; i < Deno.args.length; i++) {
let filename = Deno.args[i];
let file = await Deno.open(filename);
await Deno.copy(file, Deno.stdout);
file.close();
}Plusieurs choses ici, dâabord le Deno.args, Deno est un objet globalement injectĂ© au runtime par deno avec tout ce qui est nĂ©cessaire pour gĂ©rer les I/O du programme et bien plus encore. Ici nous utilisons la propriĂ©tĂ© args qui nous permet de rĂ©cupĂ©rer la liste des arguments passer au programme lors du run.
Ensuite nous avons le Deno.open qui permet dâouvrir un fichier, Ă ceci prĂšs que tout appel de ce genre est asynchrone, il sâagit en fait dâune promesse dâouverture du fichier. Pour rĂ©cupĂ©rer le file descriptor il faut attendre que la promesse soit rĂ©solue ou rejetĂ© par lâOS. Câest le boulot du mot clef await qui nâest pas spĂ©cifique Ă deno. Mais par contre de ce que je comprends lâintĂ©gralitĂ© du programme est considĂ©rĂ© comme asynchrone. Et donc le async dâhabitude nĂ©cessaire ne semble pas Ă avoir Ă ĂȘtre prĂ©sent.
A la ligne suivante on remarque le Deno.stdout qui est un file descriptor pointant vers la sortie standard, écrire dans ce FD revient à écrire dans la console.
De la mĂȘme maniĂšre le Deno.copy est une promesse de copy du FD du fichier dans stdout.
4 pauvres lignes et dĂ©jĂ une foule dâinformations sur le fonctionnement de deno đ.
Pour tester notre programme on va avoir besoin de fichier avec du contenu dedans. Je suis pas inspiré pour les noms.
// fichier1.txt
contenu1// fichier2.txt
contenu1Câest parti on peut lancer tout ça !
# deno run cat.ts fichier1.txt fichier2.txt
Compile file:///lab/deno/playground/cat.ts
error: Uncaught PermissionDenied: read access to "/Users/yguern/Documents/lab/deno/playground/fichier1.txt", run again with the --allow-read flag
at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
at async Object.open ($deno$/files.ts:37:15)
at async file:///Users/yguern/Documents/lab/deno/playground/cat.ts:4:16Ăa compile et fail đ„. Notre dino đŠ prĂ©fĂ©rĂ© nous explique ce qui ne va pas, nous nâavons pas les permissions de lire dans le systĂšme de fichier. Quand je vous avais dit que le runtime Ă©tait sĂ©curisĂ© je vous avais pas menti, Ă moins de lâexprimer explicitement, un programme nâa pas la possibilitĂ© de manipuler le FS ni le rĂ©seau. Il est mĂȘme possible et de rĂ©voquer des droits en plein milieu du runtime.
Bon notre programme ne vient pas des hackers russes đ», on va considĂ©rĂ© quâil est safe. Relançons avec les bonnes permissions.
# deno run --allow-read cat.ts fichier1.txt fichier2.txt
Compile file:///lab/deno/playground/cat.ts
contenu1contenu2Mieux â
Jouer avec lâAPI fetch
Un des intĂ©rĂȘts de faire du deno et pas du nodeJS est de pouvoir bosser directement avec la fetch API.
On va essayer de récupérer des Chuck Norris Jokes, pour ce nous devons faire un appel sur une API.
const res = await fetch("http://api.icndb.com/jokes/random")
const body = await res.json()
const enc = new TextEncoder();
const joke = enc.encode(body.value.joke)
await Deno.stdout.write(joke)Pas grand chose Ă dire sur ce qui est fait câest assez classique et ressemblant Ă ce qui pourrait exister sur navigateur.
Une petite nouveautĂ© est le Deno.stdout.write qui permet dâĂ©crire dans le file descriptor stdout. Celui-ci prend comme argument un Uint8Array dâoĂč lâutilisation de TextEncoder.
MĂȘme combat que tout Ă lâheure, vous nâavez pas autorisez explicitement lâaccĂšs au rĂ©seau:
# deno run joke.ts
error: Uncaught PermissionDenied: network access to "http://api.icndb.com/jokes/random", run again with the --allow-net flag
at unwrapResponse ($deno$/ops/dispatch_json.ts:43:11)
at Object.sendAsync ($deno$/ops/dispatch_json.ts:98:10)
at async fetch ($deno$/web/fetch.ts:591:27)
at async file:///Users/yguern/Documents/lab/deno/playground/joke.ts:1:13Pour se faire le flag --allow-net est là pour ça:
# deno run --allow-net joke.ts
Chuck Norris can remember the future.A vous les blagues les plus hilarantes đ.
Pour le moment le âschemeâ file:// nâest pas supportĂ© mais une PR est Ă lâĂ©tude.
Echo server
Câest le cas dâĂ©tude le plus simple lorsque que lâon dĂ©bute dans le TCP, il renvoie Ă lâexpĂ©diteur le contenu de qui est envoyĂ©.
// echo.ts
const port = Number.parseInt(Deno.args[0]) ?? 8090
const listener = Deno.listen({
port
})
console.log(`listen on 0.0.0.0:${port}`)
for await (const conn of listener) {
Deno.copy(conn, conn)
}Rien de transcendant ligne 1, on utilise le Deno.args sâil existe comme port sinon on utilise une valeur par dĂ©faut. Ceci grĂące au nullish coalescing operator ou ?? qui est voie dâadoption sur les navigateurs.
Le Deno.listen dĂ©marre un serveur TCP sur le port indiquĂ©. Lâobjet listener rĂ©alise lâinterface AsyncIterable<T> ce qui lui permet de se comporter comme un gĂ©nĂ©rateur de promesses de connexions.
Ce que for await peut alors utiliser pour gĂ©rer les connexions entrantes. Chaque conn correspond au socket Ă©tabli par lâutilisateur sur notre serveur.
Ensuite pour chaque connexion on copie la connexion vers elle mĂȘme et le tour et jouĂ©. Pour cela on utilise la mĂ©thode dĂ©jĂ Ă©tudier de Deno.copy.
LâĂ©xĂ©cution avec les bonnes permissions nous donne:
# deno run --allow-net echo.ts 8091
listen on 0.0.0.0:8091Notre serveur nous annonce quâil attend une connexion entrante.
Utilisons netcat
# nc localhost 8091
Salut Deno !
Salut Deno !Comme prĂ©vu on a un echo de ce quâon a tapĂ© đ».
Lancer un programme
A lâinstar de nodeJS nous pouvons aussi lancer un programme depuis notre typescript.
// run.ts
// On démarre dans un sous processus echo
const p = Deno.run({
cmd: ["echo", "hello đŠ"],
});
// On attend que echo finisse
await p.status();# deno run --allow-run run.ts
hello đŠLes Web Workers
Les Web Workers permettent dâĂ©xĂ©cuter des scripts sur des threads sĂ©parĂ©s.
Cela se décompose en deux parties:
Le worker
// worker.ts
self.onmessage = (event) => {
console.log(event.data)
self.close()
}On utilise lâAPI standard des WebWorker, celle ci possĂšde une mĂ©thode onmessage qui est appelĂ©e dĂšs quâun evĂšnement survient.
Cet Ă©vĂšnement est provoquĂ© du cĂŽtĂ© du main.ts par lâappelle Ă la mĂ©thode postMessage du worker.
// main.ts
const worker = new Worker('./worker.ts', { type: "module" })
worker.postMessage("Hello đŠ")Deux choses importantes ici. Dâabord la dĂ©finition du worker doit ĂȘtre dans un fichier sĂ©parĂ© et ensuite il faut impĂ©rativement et explicitement indiquer le { type: "module" } sinon ça marchera pas đ.
# deno run --allow-read main.ts
Hello đŠLe flag --allow-read est ici essentiel car on intĂ©ragie avec le systĂšme de fichier.
Le WebAssembly
Et oui on peut en faire nativement avec deno.
Je vais créér mon propre fichier .wasm, mais vous ĂȘtes libre de passer cette Ă©tape đ.
Construire un .wasm via Rust
Dâabord on se fait un petit projet en wasm via rust.
Si vous ne lâavez pas dĂ©jĂ installez cargo-generate.
cargo install cargo-generate
Il va vous permettre de générer un projet boilerplate de wasm-pack.
Ensuite installez wasm-pack lui mĂȘme:
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | shUn fois ceci fait vous pouvez gĂ©nĂ©rer votre projet. Personellement je lâai appelĂ© deno.
Jâai trĂšs lĂ©gĂšrement modifiĂ© deno/src/lib.rs
// deno/src/lib.rs
...
#[wasm_bindgen]
pub fn answer() -> i32 {
42
}Puis faire un wasm-pack build pour gĂ©nĂ©rer le fichier deno/pkg/deno_bg.wasm. Vous pouvez essayer de lâouvrir mais câest du binaire.
Utiliser le .wasm
Donc maintenant quâon a notre .wasm nous allons essayer dâappeller la mĂ©thode answer.
// main.ts
const wasm = await Deno.readFile('./deno/pkg/deno_bg.wasm');
const wasmModule = new WebAssembly.Module(wasm)
const wasmInstance = new WebAssembly.Instance(wasmModule)
console.log(wasmInstance.exports.answer())Tout dâabord on rĂ©cupĂšre le buffer du binaire dans la variable wasm. Puis on en fait un module via WebAssembly.Module, puis on transforme ce module en instance par la mĂ©thode WebAssembly.Instance pour enfin pouvoir lâutiliser.
La variable wasmInstance expose une propriĂ©tĂ© exports qui contient tout ce qui est exposĂ© vers lâextĂ©rieur par notre projet Rust. Dont notre mĂ©thode answer que lâon peut exĂ©cuter comme une fonction typescript normale.
On lit un fichier donc attention aux permissions.
# deno run --allow-read main.ts
42Lâexemple est idiot mais ça marche, la fusion deno/rust est fonctionnelle. đŠ â€ïž đŠ.
Tester

Ouais mais non, moi jâaime bien quand je peux tester mon code đ . Et justement deno propose une librairie de test dĂ©jĂ intĂ©grĂ©e.
On va encore faire du trivial mais câest juste pour le principe de fonctionnement.
Je déclare une fonction hello et je veux vérifier son comportement.
// main.ts
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
function hello() {
return "Hello đŠ"
}
Deno.test("Should return rigth string", () => {
assertEquals(hello(), "Hello đŠ")
})La premiĂšre ligne importe depuis la lib standard le systĂšme dâassertion.
Et ensuite il suffit de faire comme avec jest et déclarer son test.
Puis pour lancer les tests
# deno test module.ts
running 1 tests
test Should return rigth string ... ok (3ms)
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (3ms)Et voilà ça test â !
Metions honnorables
Formattage du code
Si vous ĂȘtes du genre Ă indenter nâimporte comment et Ă mettre les paranthĂšses nâimporte oĂč ou Ă ne pas respecter la Ă©lĂ©mentaire de ne jamais mettre de tabulations !!
Deno propose une méthode de formattage.
# deno fmt fichier_crado.tsĂa effectue les corrections et rĂ©enregistre le fichier.
Bundler
Si une source externe est composĂ© de nombreux fichiers vous pouvez demander Ă deno de crĂ©er un bundle js de lâintĂ©gralitĂ© de ces fichiers.
# deno bundle https://deno.land/std/examples/colors.ts colors.bundle.js
Bundling https://deno.land/std/examples/colors.ts
Emitting bundle to "colors.bundle.js"
8044 bytes emmited.Vous pouvez alors faire.
deno run colors.bundle.js
Hello world!Pour exécuter ce bundle.
Et la liste est encore longue. Mais je vais mâarrĂȘter lĂ cet article est dĂ©jĂ beacoup trop long pour une premiĂšre dĂ©couverte.
Conclusion
Je suis complĂštement sĂ©duit par deno, jâai hĂąte de voir ce que ça va donner dans un proche avenir.
Pour le moment je nâai pas fait de rĂ©el projet avec, mais je compte mây mettre et je vous referait un article pour donner mes impressions.
Merci de mâavoir lu et Ă la prochaine â€ïž.