Exemples essentiels de backends Node.js pour les développeurs en 2024
12 juin 2024
0 minutes de lectureEn 2024, le développement backend avec Node.js continue de se démarquer comme un environnement d’exécution puissant et flexible pour créer des applications évolutives et efficaces, d’autant plus avec l’essor d’autres environnements comme Bun.
Dans cet article, je souhaitais proposer une introduction légère à des exemples essentiels de backend Node.js, qui illustrent l’utilisation efficace des fonctionnalités avancées de JavaScript et de Node.js. De l’exploitation de la norme WHATWG Streams et de l’API Web Streams pour traiter les données efficacement à l’utilisation du module crypto intégré à Node.js pour la sécurité, en passant par la manipulation de données binaires avec les Buffers, l’encapsulation et la gestion des espaces de noms avec les Symbols, ainsi que la génération de HTML et de requêtes SQL dynamiques avec les littéraux de gabarit et les gabarits tagués, chaque section présente des extraits de code pratiques, issus de situations réelles, et des conseils.
Ces exemples mettent en évidence la polyvalence et la puissance de Node.js pour relever les défis du backend. Ils constituent également une ressource précieuse pour les développeurs qui souhaitent améliorer leurs solutions backend en 2024.
Chapitres de cet article :
La norme WHATWG Streams, l’API Web Streams et les itérables asynchrones
Utiliser le module Crypto pour valider les signatures de webhooks
Utiliser les buffers et les données binaires brutes dans Node.js
Utiliser les Symbols JavaScript pour l’encapsulation
Littéraux de gabarit et gabarits tagués pour générer du HTML et des requêtes SQL
Vous pouvez toujours consulter l’ensemble des exemples de code source de backend Node.js dans ce dépôt GitHub.
1. La norme WHATWG Streams, l’API Web Streams et les itérables asynchrones
En 2024, l’utilisation des flux s’est considérablement répandue, en particulier pour traiter des modèles de langage de grande taille dédiés à l’intelligence artificielle générative (GenAI). Le SDK OpenAI pour la complétion de chat en est un parfait exemple. Voici à quoi pourrait ressembler un exemple type de flux de données provenant de l’API du SDK OpenAI :
Nous utilisons ici le concept d’itérables asynchrones, qui a simplifié les flux de travail asynchrones dans Node.js en les alignant sur le style de programmation basé sur les promesses. L’instruction `for await…` crée une boucle qui parcourt les objets itérables asynchrones ainsi que les itérables synchrones, notamment les types intégrés String, Array, les objets semblables à des tableaux (par exemple, arguments ou NodeList), TypedArray, Map, Set et les itérables asynchrones ou synchrones définis par l’utilisateur.
Présentation des flux Web standard WHATWG dans Node.js
Les flux Web, qui font partie de la norme WHATWG Streams, ont été intégrés à Node.js et offrent une méthode robuste de gestion des flux de données. Cette norme permet aux développeurs de lire, d’écrire et de transformer efficacement des flux de données avec JavaScript.
Voyons un exemple complet d’utilisation des flux Web dans Node.js.
Dans l’exemple suivant, nous créons une fonction `createReadableStreamFromFile()` qui utilise la classe `ReadableStream` de l’API Web Streams pour créer un flux de données à partir d’un fichier. Une deuxième fonction, `consumeStreamWithAsyncIterator()`, consomme ensuite ce flux à l’aide d’un itérateur asynchrone.
Dans ce code, nous utilisons l’API Web Streams pour lire les données d’un fichier sous forme de flux. La fonction `createReadableStreamFromFile()` renvoie un objet `ReadableStream` à partir d’un chemin de fichier donné. Le flux est ensuite consommé par `consumeStreamWithAsyncIterator()`, qui le lit morceau par morceau et écrit chaque morceau sur la sortie standard. Si une erreur survient pendant la lecture, elle est interceptée et consignée dans la console.
Au fait, avez-vous remarqué la faille de sécurité dans le code ci-dessus ?
Si vous aviez installé l’extension Snyk pour VS Code, une erreur de lint ondulée vous aurait signalé une vulnérabilité de traversée de chemin dans cet exemple de code.
L’extension Snyk pour VS Code vous montrerait comment des données non sécurisées peuvent aboutir dans cet exemple de code backend Node.js, en quoi consiste cette faille de sécurité et comment la corriger grâce à des suggestions sélectionnées par l’IA à partir de projets open source actifs.

2. Utiliser le module Crypto pour valider les signatures de webhooks
Les webhooks sont devenus indispensables au développement backend moderne : ils permettent à différents services de communiquer efficacement en temps réel. Toutefois, leur utilisation croissante implique de renforcer les mesures de sécurité, notamment en validant les signatures des webhooks.
Dans le contexte des webhooks, une signature est un hash envoyé avec la charge utile du webhook. Elle est calculée à l’aide d’une clé secrète que seuls l’expéditeur et le destinataire connaissent. Le destinataire peut alors calculer le hash de son côté et le comparer à la signature afin de vérifier l’authenticité du webhook.
Le module Crypto de Node.js offre de nombreuses fonctionnalités cryptographiques, dont un ensemble de wrappers pour les fonctions de hachage, HMAC, chiffrement, déchiffrement, signature et vérification d’OpenSSL. Le HMAC (code d’authentification de message fondé sur un hash) est particulièrement utile pour valider les signatures de webhooks.
Décomposons l’extrait de code fourni pour comprendre son fonctionnement :
Dans cet extrait, la méthode `crypto.createHmac` sert à créer un objet HMAC. Elle prend deux paramètres : l’algorithme à utiliser (ici, `sha256`) et la clé secrète.
L’objet HMAC est ensuite mis à jour avec le corps brut de la requête webhook à l’aide de la méthode `hmac.update`. Cette méthode peut être appelée plusieurs fois à mesure que de nouvelles données arrivent sous forme de flux.
La méthode `digest` génère ensuite le hash. Elle ne peut être appelée qu’une seule fois sur l’objet HMAC et renvoie le hash calculé. Le paramètre `hex` indique à la méthode de renvoyer le hash au format hexadécimal.
Présentons maintenant le concept de comparaison sécurisée de chaînes. Le hash calculé (digest) et la signature reçue dans l’en-tête de la requête webhook sont ensuite comparés à l’aide de la méthode `crypto.timingSafeEqual`. Cette méthode compare deux buffers de manière sécurisée contre les attaques temporelles, ce qui la rend idéale pour comparer des résultats cryptographiques.
Les attaques temporelles sont un type d’attaque par canal auxiliaire dans lequel un attaquant tente de compromettre un système en analysant le temps nécessaire à l’exécution d’algorithmes cryptographiques. En utilisant une méthode sécurisée contre les attaques temporelles, comme `crypto.timingSafeEqual`, nous nous protégeons contre ce type d’attaque. Si le digest et la signature ne correspondent pas, une erreur est générée, indiquant que la requête webhook n’est peut-être pas authentique.
3. Utiliser les buffers et les données binaires brutes dans Node.js
L’API Buffer de Node.js est un outil puissant qui permet aux développeurs de manipuler directement des données binaires. Que vous ayez besoin de lire un fichier, d’analyser une image ou de traiter des données brutes, l’API Buffer fournit des méthodes pour effectuer ces tâches efficacement.
Découvrons quelques méthodes courantes de l’API Buffer dans Node.js, comme `.from`, `.alloc` et `.write`, qui permettent de créer et de manipuler des objets buffer. La méthode `.from` crée un nouveau buffer à partir des données transmises en argument, `.alloc` crée un buffer d’une taille spécifiée et `.write` permet d’écrire des données dans un buffer. La méthode `.concat`, quant à elle, sert à concaténer une liste d’instances Buffer.
Analyser une image avec l’API OpenAI et l’API Buffer de Node.js
Dans le prochain exemple de code backend Node.js, nous utilisons l’API Buffer pour lire un fichier image et l’analyser avec l’API OpenAI. Nous commençons par importer les modules nécessaires et créer une instance du client de l’API OpenAI :
Nous lisons ensuite le fichier image dans un buffer :
Nous validons ensuite le type de l’image en comparant sa signature à celle d’un fichier PNG connu :
Enfin, nous générons un texte alternatif descriptif pour l’image à l’aide de l’API OpenAI :
Considérations de sécurité lors de l’utilisation de l’API Buffer
L’utilisation de l’API Buffer implique plusieurs considérations de sécurité. Une mauvaise gestion des données binaires peut entraîner des vulnérabilités potentielles, comme des erreurs de dépassement ou de sous-dépassement de buffer. Veillez toujours à valider les entrées et à gérer correctement les erreurs.
Soyez également attentif aux vulnérabilités potentielles, comme le constructeur `Buffer`, désormais obsolète, qu’il convient d’éviter au profit d’alternatives plus sûres telles que `Buffer.from` ou `Buffer.alloc`.
4. Utiliser les Symbols JavaScript pour l’encapsulation
Les Symbols sont un type de données primitif introduit dans ES6 (ECMAScript 2015) qui représente des identifiants uniques et immuables. Ils sont créés avec la fonction `Symbol()`, qui accepte éventuellement une description (une chaîne) pouvant servir au débogage, sans toutefois modifier l’unicité du symbole. Les Symbols servent principalement à créer des clés de propriétés uniques pour les objets, qui n’entrent en conflit avec aucune autre propriété, y compris les propriétés héritées. Ils sont donc particulièrement utiles pour définir des propriétés privées ou spéciales d’objets sans risquer de conflit de noms.
Voici un exemple de code backend Node.js montrant comment utiliser les Symbols pour créer une propriété privée dans une classe et encapsuler des données :
De plus, les Symbols ne sont pas accessibles lors de l’énumération des propriétés d’un objet (avec des boucles `for...in` ou `Object.keys()`, par exemple). Ils peuvent donc, dans une certaine mesure, servir à simuler des propriétés et des méthodes privées pour les objets. Il faut toutefois noter qu’ils ne sont pas véritablement privés et restent accessibles à l’aide de méthodes de réflexion comme `Object.getOwnPropertySymbols()`.
JavaScript définit un ensemble de Symbols bien connus représentant des comportements internes du langage que les développeurs peuvent personnaliser. Par exemple, voici comment implémenter un itérateur pour un objet personnalisé à l’aide de `Symbol.iterator` :
Utilisation des Symbols JavaScript par Fastify
Examinons un exemple plus concret avec le framework d’applications web Fastify et la façon dont le projet utilise les Symbols. Nous allons notamment nous pencher sur l’architecture de plugins de Fastify. L’un des aspects clés de la conception de Fastify est sa fonctionnalité d’encapsulation, qui permet aux développeurs de créer des contextes d’application isolés à l’aide de plugins. C’est essentiel pour créer des applications à grande échelle, où les conflits d’espaces de noms peuvent devenir problématiques.
Fastify utilise des Symbols pour identifier de manière unique les propriétés et méthodes internes, afin d’éviter qu’elles interfèrent avec les propriétés définies par l’utilisateur ou celles d’autres plugins. Voici un exemple simplifié, inspiré de l’utilisation réelle des Symbols dans le code source de Fastify pour encapsuler les métadonnées d’un plugin :
Dans l’exemple ci-dessus, `pluginMeta` est un Symbol utilisé par Fastify pour associer des métadonnées aux fonctions de plugin. Ces métadonnées comprennent les options et le nom du plugin, ainsi que d’autres informations dont le framework pourrait avoir besoin en interne. La fonction `registerPlugin` simplifie l’association des métadonnées à un plugin avant son enregistrement dans une instance Fastify. Ces métadonnées sont ensuite accessibles au sein du framework Fastify, tout en restant isolées de l’interface publique du plugin et de l’espace de noms global de l’application.
L’utilisation des Symbols JavaScript pour des métadonnées internes comme celles-ci présente plusieurs avantages dans un framework tel que Fastify :
Encapsulation : elle empêche les détails internes de se retrouver dans l’espace utilisateur, préservant ainsi la clarté et la simplicité de l’API publique.
Sécurité : elle réduit le risque d’interférences accidentelles entre les plugins, ou entre un plugin et le cœur du framework, car les Symbols ne sont pas accessibles par l’énumération ordinaire des propriétés d’un objet.
Clarté : elle distingue clairement les mécanismes internes du framework des API exposées aux développeurs, ce qui facilite la maintenance et l’évolution du code source de Fastify.
5. Littéraux de gabarit et gabarits tagués pour générer du HTML et des requêtes SQL
Introduits dans ES6, les littéraux de gabarit offrent une syntaxe plus lisible et concise pour créer des chaînes en JavaScript. Vous les utilisez peut-être déjà pour écrire des chaînes et y intégrer des expressions dynamiques, comme `hello ${name}`. Fini la concaténation de chaînes.
On observe aujourd’hui une tendance croissante à utiliser les littéraux de gabarit sous forme de gabarits tagués pour générer du HTML et des requêtes SQL dans les backends Node.js. Voici deux exemples concrets de code backend Node.js et SSR :
Modèles balisés pour générer des requêtes SQL avec la bibliothèque PostgreSQL de Vercel
La gestion des requêtes SQL dans Node.js peut souvent produire du code verbeux et source d’erreurs, surtout lorsqu’il faut insérer dynamiquement des valeurs dans les requêtes. Le package PostgreSQL de Vercel (`@vercel/postgres`) propose une méthode plus concise et plus sûre pour formater les requêtes SQL à l’aide de littéraux de gabarit :
La fonction de littéral de gabarit balisé SQL insère de manière sécurisée la variable `jediName` dans la requête SQL, empêchant ainsi les attaques par injection SQL.
Modèles balisés pour générer du HTML sur des serveurs Fastify
Comme pour les requêtes SQL, `fastify-html` est un plug-in Fastify qui permet aux développeurs d’utiliser des modèles balisés pour générer du contenu HTML directement dans les gestionnaires de routes. Cette méthode peut s’avérer particulièrement utile pour le rendu côté serveur (SSR) ou la génération de contenu HTML dynamique (quelqu’un a dit htmx ?) :
Que sont les littéraux de gabarit étiquetés ?
Dans le prolongement du concept des littéraux de gabarit, les littéraux de gabarit étiquetés sont une fonctionnalité de JavaScript qui vous permet de définir une fonction personnalisée (le « tag ») pour traiter les littéraux de gabarit. Cette fonction reçoit le littéral de gabarit sous la forme d’un tableau de chaînes et les expressions interpolées sous forme d’arguments distincts. Vous pouvez ainsi manipuler le gabarit et ses valeurs avant la création de la chaîne finale.
Pour conclure
Si cet article vous a plu, découvrez aussi les bonnes pratiques pour créer un package npm moderne en tenant compte de la sécurité et 10 bonnes pratiques pour conteneuriser des applications web Node.js avec Docker.
Et si les récits autour de la sécurité vous intéressent, pensez à bien vous préparer à combattre les attaques de la chaîne logistique sur npm en appliquant les pratiques de sécurité pour les développeurs mentionnées dans cet article.
Enfin, n’oubliez pas de découvrir Snyk avec un compte gratuit pour commencer à sécuriser votre code Node.js, vos dépendances et vos images de conteneurs Docker.


