JSJS Memo🎙️ Coach

Node

Node.js est un environnement d'exécution JavaScript côté serveur, basé sur le moteur JavaScript V8 de Google Chrome. Il permet d'exécuter du code JavaScript en dehors d'un navigateur, et est conçu pour construire des applications réseau et serveur en utilisant une architecture non-bloquante et asynchrone. Il utilise un seul thread principal pour exécuter le code JavaScript. C'est-à-dire que tout le code (les fonctions, les boucles, etc.) est exécuté séquentiellement sur le thread principal (grâce à la boucle d'événements, ou event loop), il ne se limite pas à ce seul thread pour accomplir des tâches en arrière-plan. Node.js délègue certaines opérations lourdes ou asynchrones à des mécanismes libuv, comme un pool de threads ou des fonctions système non bloquantes. Types d'opérations déléguées au thread pool Certaines opérations en Node.js nécessitent un travail asynchrone en arrière-plan. Ces tâches sont envoyées au thread pool géré par libuv, ce qui permet au thread principal de rester libre pour traiter d'autres requêtes. Exemples d'opérations utilisant le thread pool : Opérations de système de fichiers (File System) : Les appels comme fs.readFile, fs.writeFile ou fs.stat dépendent de l'I/O disque. Ces opérations sont envoyées au thread pool.

{EN}

asynchronous architecture.

Types of Operations Delegated to the Thread Pool

incoming requests. File System Operations:

Event loop

L'event loop (boucle d'événements) est un concept central dans le fonctionnement de Node.js. Il permet d'exécuter de manière asynchrone des opérations et de gérer les événements.

Comment ça fonctionne

L'event loop de Node.js démarre en créant une file d'attente des événements (event queue) pour stocker les événements à traiter.

Ensuite Event loop commence ensuite à vérifier la file d'attente des événements. Si la file d'attente n'est pas vide, elle récupère le prochain événement disponible. Une fois qu'un événement est récupéré, la boucle d'événements traite cet événement en exécutant la fonction callback associée à cet événement. Cette callback peut effectuer des opérations asynchrones. Lorsqu'une opération asynchrone est déclenchée, Node.js ne bloque pas le thread principal. Au lieu de cela, il planifie cette opération pour qu'elle soit exécutée en arrière-plan. Node.js utilise des mécanismes tels que les callbacks, les promesses ou les fonctions async/await pour gérer le traitement asynchrone. Attente d'événements : Après avoir traité un événement et ses opérations asynchrones associées, la boucle d'événements revient à la file d'attente pour récupérer le prochain événement. Si la file d'attente est vide, la boucle d'événements attend qu'un nouvel événement soit ajouté à la file Ce processus de récupération, de traitement des événements et d'exécution asynchrone se répète en boucle tant que l'application Node.js est active.

Event loop

Timers : exécute les callbacks et setTimeout/setInterval enregistrés I/O callbacks : exécute tous les callbacks sauf close, timers et setImmediate Idle, prepare : interne Poll : récuperation ou attente des nouveaux evenements I/O Check : exécute les callbacks setImmediate enregistrés Close callbacks : socket.on('close', …)

{EN}

How It Works

be handled.

manage these asynchronous operations.

Comparaison == / ===

L'égalité faible (==) vérifie uniquement l'égalité de valeur

L'égalité stricte (===) vérifie l'égalité de valeur et le type de données (c'est-à-dire une valeur doit être de même type)

Undefinied/ NULL

Undefined : la variable a été déclarée, mais n'a pas encore attribué à une valeur NULL c'est la valeur d'affectation

TypeOf

TypeOf est un opérateur utilisé pour renvoyer la description du type de variable.

Typeof null renvoie "object", ce qui est une particularité de JavaScript et est considéré comme une erreur de conception du langage. Les tableaux (Array) et les objets (Object) sont tous deux renvoyés en tant que "object". Il n'y a pas de distinction spécifique entre les tableaux et les objets lors de l'utilisation de typeof. Les fonctions sont renvoyées en tant que "function", ce qui est un type de données distinct.

Mutable

Un objet mutable est ainsi un objet qui peut être modifié, dont on peut changer les propriétés une fois qu'il a été défini.

Immutable

Les objets immuables (immutable) sont des objets dont l'état ne peut être modifié une fois l'objet créé.( Chaînes de caractères et nombres sont immuables.)

Ce concept permet de s'assurer de la valeur des variables du début à la fin de nos actions. Cela va permettre d'avoir un code plus robuste et plus stable, donc moins de bugs et moins de maintenances. Tout au long de notre script les variables ne peuvent être modifiées.

V8

V8 est un moteur Javascript, conçu pour analyser et exécuter du code JavaScript avec des performances optimales.

V8 est responsable de l'interprétation et de l'exécution du code JavaScript. Il utilise des techniques d'optimisation avancées telles que la compilation Just-in-Time (JIT) pour accélérer l'exécution du code. V8 effectue des analyses statiques et dynamiques pour générer un code machine optimisé qui s'exécute rapidement

Comment fonctionne V8 avec NodeJS

V8 joue un rôle dans le fonctionnement de Node.js. Node.js est un environnement JavaScript construit sur le moteur V8. En résumé, V8 est intégré à Node.js pour fournir l'environnement d'exécution JavaScript. Il analyse, compile et exécute le code JavaScript, gère les événements et les E/S de manière asynchrone, et applique des optimisations JIT(just-in-time) pour améliorer les performances. Grâce à V8, Node.js offre une exécution JavaScript rapide et événementielle.

Dans une function arrow, this reste tjrs this de la fonction parente Arrow Vs Regular function

La fonction fléchée à deux particuliers : La syntaxe est beaucoup plus rapide Elle va fixer la valeur de this automatiquement

Différence entre arrow et normal function

les principales différences entre les deux :

Syntaxe : Les fonctions régulières sont définies à l'aide du mot-clé function, suivi du nom de la fonction et de ses paramètres entre parenthèses, puis du corps de la fonction

entre des accolades {}. Les fonctions fléchées sont définies à l'aide de la notation () =>
      {}, avec les paramètres entre les parenthèses et le corps de la fonction entre des
      accolades {}.

Liaison du mot-clé this : Dans les fonctions régulières, le mot-clé this est déterminé par la façon dont la fonction est appelée, ce qui peut parfois causer des problèmes de liaison. Dans les fonctions fléchées, le mot-clé this est lié au contexte lexical dans lequel la fonction est définie, ce qui évite les problèmes de liaison.

Utilisation de arguments : Les fonctions régulières ont accès à l'objet arguments, qui contient les paramètres passés à la fonction, ce qui n'est pas possible avec les fonctions fléchées.

Une fonction normale a un this dynamique, défini par l'objet qui l'appelle. Tu peux le modifier avec call, apply ou bind.

Une fonction fléchée a un this fixe, hérité du contexte elle est définie. Tu ne peux pas le changer, même avec bind.

Call Vs apply Vs Bind

call appelle la fonction immédiatement avec des arguments séparés(retour fonction) apply appelle la fonction immédiatement avec les arguments dans un tableau(retour fonction)

bind n'appelle pas la fonction, il retourne une nouvelle fonction avec this fixé(retour nouvelle fonction) Asynchrone

Asynchrone : un fonctionnement non bloquant qui va nous permettre de lancer une tâche et d'avancer dans le programme le temps.

Le traitement asynchrone est pour optimiser les performances et la réactivité des applications. Node.js gère le traitement asynchrone

Callbacks :est utilisée pour gérer les opérations asynchrones. Lorsqu'une opération asynchrone est effectuée, nous spécifions une fonction de callback qui sera appelée une fois que l'opération est terminée. Cela permet à Node.js de poursuivre l'exécution du code sans attendre la fin de l'opération asynchrone. Promise est une alternative aux fonctions de callback pour gérer le traitement asynchrone de manière plus structurée et facile à lire. La function promise représente la réussite ou l'échec d'une opération asynchrone et permettent d'attacher des fonctions de callback pour traiter ces résultats. Async/await :pour simplifier la gestion du traitement asynchrone. Elle permet d'écrire du code asynchrone de manière séquentielle, en utilisant les mots-clés async et await,

Synchrone

Synchrone signifie qu'une opération est exécutée de manière séquentielle, c'est-à-dire que le programme attend que chaque opération se termine avant de passer à la suivante. Cela peut être utile lorsque vous devez attendre le résultat d'une opération avant de pouvoir continuer.

Callback

Callback est une fonction qui est passée en tant qu'argument à une autre fonction et qui est appelée ultérieurement lorsque cette fonction a fini de s'exécuter.

Le callback permet de gérer les opérations asynchrones en spécifiant une fonction à appeler une fois que l'opération asynchrone est terminée et que les résultats sont disponibles.

Callback Vs Promises

Callbacks et Promises sont deux mécanismes qui permettent de gérer l'exécution asynchrone du code en JavaScript. Voici les principales différences entre les deux :

Syntaxe : Les callbacks sont des fonctions qui sont passées en tant qu'argument à une autre fonction. Les Promises sont des objets qui sont créés avec le constructeur Promise. Gestion des erreurs : Avec les callbacks, la gestion des erreurs peut être complexe et sujette à des problèmes de rappel en cascade (callback hell). Les Promises permettent une gestion plus facile des erreurs en utilisant les méthodes then() et catch() pour gérer les cas de réussite et d'échec.

Enchaînement : Avec les callbacks, l'enchaînement de plusieurs opérations asynchrones peut être difficile et peut entraîner une indentation excessive du code (callback hell). Les Promises permettent un enchaînement plus facile des opérations asynchrones en utilisant les méthodes then() et catch() pour retourner de nouvelles Promises.

Lisibilité du code : Les Promises peuvent rendre le code plus lisible et plus facile à comprendre en utilisant une syntaxe plus claire et en évitant les rappels en cascade (callback hell).

Promise

Promise est un objet qui est utilisé pour réaliser des traitements de façon asynchrone. Une Promise représente une valeur qui peut être disponible maintenant, dans le futur ou jamais.

Une Promise peut être dans l'un des trois états suivants :

pending (en attente) : C'est l'état initial de la Promise. Cela signifie que la promesse n'est ni remplie ni rompue. Elle est en attente de la réalisation d'une action asynchrone. fulfilled (tenue) : Cela signifie que l'opération asynchrone a réussi et que la Promise a été résolue avec une valeur. rejected (rompue) : Cela signifie que l'opération asynchrone a échoué et que la Promise a été rejetée avec une raison.

Une Promise peut également être dans l'état settled (acquittée), ce qui signifie que la promesse est tenue ou rompue mais qu'elle n'est plus en attente.

Callback Hell

CallBack Hell : c'est une fonction imbriqué, on utilise Promise.all() pour avoir la lisibilité des codes si les fonctions ne dépendent pas l'une après l'autre.

Async et Await

Async" et "Await sont deux opérateurs pour simplifier la gestion du traitement asynchrone. Le fonctionnement technique : On place "async" en premier lieu lorsque l'on déclare une fonction pour préciser que celle-ci est asynchrone. On place "await" juste avant d'appeler une fonction asynchrone, ce qui aura pour but d'attendre la réponse de cette dernière.

Async et Await ne remplacent pas les Promises, ils permettent uniquement de simplifier l'appel de celles-ci.

Promise.all attend que tout réussisse, sinon ça échoue

Promise.allSettled attend que tout se termine, succès ou erreur

Promise.race prend le premier qui finit (succès ou erreur)

Promise.any prend le premier qui réussit (ignore les erreurs)

Les nouveautés de ES6

Les variables déclarées let et const Les classes Modules(import / export) Les fonctions fléchées Paramètres par défaut pour les fonctions Rest paramètre Spread paramètre Les templates Strings (back sticks) Les destructuring

CONST

Const a besoin d'être déclaré avec une valeur initiale. La portée de const est celle du bloc the block On ne peut pas changer sa valeur après déclaration Avec const pour objet, on peut changer les propriétés d'un objet existant mais ne pas Réassigner un nouvel objet Const n'est pas hosting LET Il n'est pas obligatoire de déclarer une variable avec sa valeur initiale. On peut modifier sa valeur après sa déclaration Elle est limitée à celle du bloc courant LET n'est pas hosting

Var

var ancien, portée fonction, peut être redéclaré et modifié à éviter REST Operator

L'opérateur REST(…variable) permet de convertir(ou transformer) la valeur libre(nombre indéfini) d'arguments sous forme d'un tableau.

SPREAD Operator

L'opérateur SPREAD, c'est l'inverse de l'opérateur REST, il prend le tableau, il va nous transformer ce tableau en valeur libre

Avec SPREAD, On pourra aussi créer un nouvel Tableau à partir d'un ancien

Destructruring

Destructuring qui permet d'extraire des données d'un tableau ou d'un objet grâce à une syntaxe dont la forme ressemble à la structure du tableau ou de l'objet.

Destructuring Arrays Destructuring Objet

Remarque

La différence entre destructuring tableau et Objet, Pour les tableaux on repose sur les notions d'ordre (on connait l'élément qui se trouve en première position, n position, quand on faisait une destructuring du tableau, on pouvait dire la variable a comme l'écrit en première elle va contenir la première valeur de mon Tableau) Dans les objets, il n'y a pas d'ordre, ce qu'important dans l'objet c'est de la propriété. Buffer Buffer est mecanisme pour manipuler des données.Il est utilisé pour stocker et manipuler des octets.

Un Buffer peut être créé en utilisant plusieurs méthodes, telles que Buffer.from(), Buffer.alloc(), Pour les Accès aux données du Buffer : elles peuvent être accédées en utilisant des index ou des méthodes d'accès telles que buf[index], buf.readUInt8(), etc. Manipulation des données du Buffer : Les données dans un Buffer peuvent être modifiées en utilisant des méthodes telles que buf.write(), buf.fill(), buf.slice(), etc. Conversion du Buffer en d'autres formats : Un Buffer peut être converti en différentes représentations, telles que des chaînes de caractères, des tableaux d'octets, des nombres, etc

Stream

Les Stream sont un concept pour la manipulation de données, lorsque les données sont volumineuses ou doivent être traitées en continu. Les streams permettent de traiter les données morceau par morceau (ou "chunks") au lieu de les charger en totalité en mémoire. Cela permet de réduire la consommation de mémoire et d'améliorer les performances. Les principaux types de streams :

Readable Streams : Les Readable Streams représentent une source de données à partir de laquelle on pourra lire des données. Par exemple, la lecture d'un fichier, la récupération de données depuis un serveur ou la diffusion de données en continu sur un réseau.

Writable Streams : Les Writable Streams représentent une destination on pourra écrire des données. Par exemple, l'écriture de données dans un fichier, l'envoi de données à un serveur ou l'affichage de données dans la console.

Duplex Streams : Les Duplex Streams sont des streams bidirectionnels qui peuvent être à la fois en lecture et en écriture. Ils permettent de lire et d'écrire des données simultanément. Par exemple, une connexion réseau qui reçoit et envoie des données en même temps.

Transform Streams : Les Transform Streams sont des streams duplex qui permettent de modifier ou de transformer les données en cours de transfert. Ils prennent en entrée des données, effectuent une transformation et produisent des données modifiées en sortie. Par exemple, la compression de données, la conversion de formats, etc.

Pour utiliser les streams en Node.js, on peut utiliser les modules intégrés tels que fs pour les opérations de fichiers, http pour les opérations réseau, et zlib pour la compression/décompression, entre autres. Les streams peuvent être utilisés de manière séquentielle en utilisant des méthodes de lecture, d'écriture et de manipulation des données, ou en utilisant des événements tels que data, end, error, etc. ExpressJS

C'est un framework node JS , c'est-à-dire c'est un Framework qui vous permet de gérer la partie routing, on a aussi la possibilité de créer un middleware

Express

Ultra connu, énorme écosystème, simple à prendre en main Très flexible, peu opinionated Middleware ecosystem très mature Moins performant nativement Validation, schema, typing, logs structurés = souvent à rajouter toi-même

Fastify

Plus moderne et plus performant Basé sur les schemas (JSON Schema) pour validation/serialization Meilleur outillage natif pour perf, logs, plugins Architecture plus clean pour APIs robustes Un peu plus strict, donc légère courbe d'apprentissage

En vrai:

- Express si tu veux aller vite, que l'équipe le maîtrise déjà, ou sur un projet simple / legacy / très intégré à l'écosystème existant - Fastify si tu construis une API moderne, scalable, propre, avec focus perf, validation stricte et maintenabilité

Routage

Routage fait référence à la détermination de la méthode dont une application répond à une demande client adressée à un noeud final spécifique, c'est-à-dire un URI (ou chemin) et une méthode de demande HTTP (GET, POST, etc.).

La définition de la route utilise la structure suivante : : app est une instance d'express. METHOD est une méthode de demande HTTP. PATH est un chemin sur le serveur. HANDLER est la fonction exécutée lorsque la route est mise en correspondance.

Il existe une méthode de routage spéciale, app.all(), qui n'est pas dérivée d'une méthode HTTP. Cette méthode est utilisée pour charger des fonctions middleware à un chemin d'accès pour toutes les méthodes de demande.

Middleware

Les fonctions de middleware sont des fonctions qui peuvent accéder à l'objet Request (req), l'objet response (res) et à la fonction middleware suivant dans le cycle demande-réponse de l'application. La fonction middleware suivant est couramment désignée par une variable nommée next. Elle effectuent les tâches comme:

Exécuter tout type de code. Apporter des modifications aux objets de demande et de réponse. Terminer le cycle de demande-réponse. Appeler le middleware suivant dans la pile.

Si la fonction middleware en cours ne termine pas le cycle de demande-réponse, elle doit appeler la fonction next() pour transmettre le contrôle à la fonction middleware suivant. Sinon, la demande restera bloquée. Pour charger la fonction middleware, appelez app.use() en spécifiant la fonction middleware. Par exemple, le code suivant charge la fonction middleware myLogger avant la route au chemin racine (/).

Une application Express peut utiliser les types de middleware suivants :

Middleware niveau application Middleware niveau routeur Middleware de traitement d'erreurs Middleware intégré Middleware tiers

Middleware niveau application

Liez le middleware niveau application à une instance de l'objet app object en utilisant les fonctions app.use() et app.METHOD(), METHOD est la méthode HTTP de la demande que gère la fonction middleware (par exemple GET, PUT ou POST)

Cet exemple illustre une fonction middleware sans chemin de montage. La fonction est exécutée à chaque fois que l'application reçoit une demande. Cet exemple illustre une fonction middleware montée sur le chemin /user/:id. La fonction est exécutée pour tout type de demande HTTP sur le chemin/user/:id.

Voici un exemple de chargement d'une série de fonctions middleware sur un point de montage, avec un chemin de montage. Il illustre une sous-pile de middleware qui imprime les infos de demande pour tout type de demande HTTP adressée au chemin /user/:id.

Middleware niveau routeur

Le middleware niveau routeur fonctionne de la même manière que le middleware niveau application, à l'exception près qu'il est lié à une instance de express.Router().

Middleware de traitement d'erreurs

Définissez les fonctions middleware de traitement d'erreurs de la même façon que d'autres fonctions middleware, à l'exception près qu'il faudra 4 arguments au lieu de 3, et plus particulièrement avec la signature (err, req, res, next)) : Middleware intégré

Depuis la version 4.x, Express ne dépend plus de Connect. A l'exception de express.static, toutes les fonctions middleware précédemment incluses à Express' font désormais partie de modules distincts. express.static(root, [options]) La seule fonction middleware intégrée dans Express est express.static. Cette fonction est basée sur serve-static et a la responsabilité de servir les actifs statiques d'une application Express.

Middleware tiers

Utilisez un middleware tiers pour ajouter des fonctionnalités à des applications Express. Installez le module Node.js pour la fonctionnalité requise, puis chargez-le dans votre application au niveau application ou au niveau router.

Next()

La fonction next() en Express sert à passer le contrôle au middleware suivant dans la chaîne. Si un middleware ne termine pas la requête avec une réponse (res.send, res.json, etc.), il doit appeler next() pour éviter de bloquer la requête. On peut aussi utiliser next(err) pour transmettre une erreur au middleware de gestion des erreurs. Child Process Child process qui permet de créer un sorte de sous process nodejs qui va pouvoir être gérer par notre instance principal. Il offre différentes méthodes pour créer, contrôler et communiquer avec les processus enfants.

Création d'un processus enfant : Le module child_process fournit plusieurs méthodes pour créer un processus enfant. Les méthodes couramment utilisées sont exec(), spawn() et fork(). exec() : Cette méthode permet d'exécuter une commande système dans un processus enfant et de récupérer sa sortie. spawn() : Cette méthode permet de démarrer un nouveau processus indépendant et de communiquer avec lui à travers des flux de données (stdin, stdout, stderr). fork() : Cette méthode est spécifiquement utilisée pour créer un processus enfant qui exécute des fichiers JavaScript. Il utilise le module child_process pour communiquer avec le processus parent. Communication avec le processus enfant : Une fois le processus enfant créé, vous pouvez communiquer avec lui à l'aide des flux de données (stdin, stdout, stderr). Vous pouvez envoyer des données au processus enfant via stdin, lire la sortie du processus enfant via stdout, et gérer les erreurs via stderr. Gestion des événements : Le module child_process émet des événements tels que 'exit', 'close', 'error', etc., qui permettent de gérer le cycle de vie du processus enfant et de prendre des mesures appropriées. Communication asynchrone : Le module child_process permet une communication asynchrone avec les processus enfants, ce qui signifie que vous pouvez envoyer des données, attendre la réponse du processus enfant et traiter les résultats de manière non bloquante.

Worker Cluster

Worker cluster qui permet d'utiliser de Threads supplémentaires enfin d'exécuter le javascript en parallèle Node.js est principalement conçu pour être exécuté sur un seul thread

Optimization Clustering

Difference Timer en Node Blocking et non blocking Lorsqu'une opération est bloquante, l'exécution du programme est suspendue jusqu'à ce que l'opération soit terminée. Aucune autre instruction ne peut être exécutée.

Dans une opération non bloquante, l'exécution du programme se poursuit immédiatement après le lancement de l'opération, sans attendre sa fin. Une fois que l'opération d'E/S est terminée, une notification ou un événement est déclenché pour signaler que les données sont disponibles. Cela permet à l'application de continuer à exécuter d'autres tâches sans attendre la fin de l'opération d'E/S. L'utilisation d'opérations d'E/S non bloquantes est bénéfique pour les applications qui doivent gérer de nombreuses connexions simultanées, car elles peuvent traiter des requêtes sans bloquer l'exécution du programme. Node.js est particulièrement adapté aux opérations d'E/S non bloquantes car il utilise une boucle d'événements (event loop) pour gérer les requêtes.

EventEmitter et Observable

EventEmitter est une classe de base de Node.js qui permet de créer des objets qui peuvent émettre des événements et de les gérer. Elle est utilisée pour implémenter la communication asynchrone entre différentes parties d'une application.

Comment fonctionne EventEmitter

Création d'une instance d'EventEmitter en instanciant la classe EventEmitter. Enregistrement un Listener pour un événement, avec la méthode on(eventName, callback) qui prend deux paramètre, le nom d'événement et une function callback Émission d'un événement avec la méthode emit(eventName, [args]) pour émettre un événement Suppression des listeners avec la méthode

myEmitter.removeListener('eventName', listener);

Observable est une interface qui permet de créer des objets qui peuvent être observés par d'autres parties de l'application. Elle est principalement utilisée pour la programmation réactive, qui consiste à créer des programmes qui réagissent aux changements de données au fur et à mesure qu'ils se produisent.

Libuv

Libuv est une bibliothèque utilisée par Node.js pour gérer les opérations d'Entrée/Sortie (E/S), asynchrones et les événements. Elle fournit une abstraction de bas niveau pour les fonctionnalités liées au système d'exploitation, telles que les sockets, les fichiers, les timers, les événements, Comment fonctionne Libuv

Libuv fournit un event loop qui gère l'exécution des opérations asynchrones et des événements. L'event loop est responsable de la gestion des requêtes entrantes, de l'exécution des callbacks et de la coordination des différentes tâches. Gestion des événements asynchrones : Libuv utilise des mécanismes de bas niveau fournis par le système d'exploitation, tels que les sockets, les fichiers, les threads, pour gérer les opérations d'E/S asynchrones.

Pour les Queues: Libuv utilise différentes files d'attente pour gérer les événements et les opérations d'E/S asynchrones. Threads et ThreadPool : Libuv utilise des threads pour gérer certaines opérations d'E/S bloquantes et les travaux intensifs. Par exemple, lorsqu'une opération d'E/S bloquante est détectée, Libuv utilise un thread distinct pour gérer cette opération afin de ne pas bloquer la boucle d'événements principale. Il dispose également d'un ThreadPool pour gérer des tâches de fond telles que le calcul intensif.

API REST

API (State Architectural Transfer) c'est une approche architecturale de la conception de services Web. elle est un style architectural destiné à la construction de systèmes distribués basés sur l'hypermédia et utilise HTTP comme protocole d'application.

L'un des autres aspects uniques des API REST est qu'elles sont stateless(sans état) ce qui signifie que le serveur ne sauvegarde aucune des requêtes ou réponses précédentes.

HATEOAS

HATEOAS est un principe des API REST le serveur fournit, en plus des données, des liens (hypermedia) permettant au client de savoir quelles actions il peut faire ensuite.

Idée clé

- Le client n'a pas besoin de connaître toutes les routes à l'avance - Il suit les liens fournis par l'API

Hypermedia

Dans l'API REST (Representational State Transfer), l'utilisation de l'Hypermédia permet de fournir des informations sur les actions possibles sur les ressources de l'API et sur la façon dont elles sont liées entre elles.

Ou permet aux clients de l'API de naviguer de manière dynamique dans les ressources de l'API et de découvrir les actions possibles

Bonne Pratique n°1 Utiliser les verbes HTTP

GET : Utilisé pour récupérer des données.Par exemple, pour obtenir les détails d'un utilisateur(cachable), POST : Utilisé pour créer une nouvelle ressource. Par exemple, pour créer un nouvel utilisateur, vous enverriez une requête POST avec les informations de l'utilisateur dans le corps de la requête. PUT : Utilisé pour mettre à jour une ressource existante. Par exemple, pour mettre à jour les détails d'un utilisateur, vous pourriez utiliser une requête PUT sur l'URL correspondante avec les nouvelles informations de l'utilisateur dans le corps de la requête. DELETE : Utilisé pour supprimer une ressource spécifiée. PATCH pour effectuer des mises à jour partielles OPTIONS pour obtenir les options de communication disponibles pour une ressource

Bonne Pratique n°5 Utiliser correctement les codes de retour :

Bonne Pratique n°6 Format de URL :

utiliser le verbe « GET » et l'URL devrait avoir la forme /users/{user-ID} où {user-ID} est

l'ID de l'utilisateur spécifique.

REST et RESTFULL

Une API est RESTful quand elle respecte le principe d'architecture REST. Ce principe d'architecture s'applique aux services Web.

REST est un style architectural qui définit les principes et les contraintes pour la conception de services web, tandis que RESTful est un terme utilisé pour décrire une application ou un service web qui est conforme à ces principes. REST est un concept plus large qui englobe les principes architecturaux, tandis que RESTful se réfère spécifiquement à une application ou à une API qui suit ces principes.

Une API RESTful respecte les contraintes de REST, telles que l'identification des ressources par des URLs, l'utilisation des verbes HTTP appropriés, et la conception stateless. Elle est considérée comme une implémentation correcte et conforme de l'architecture REST.

REST est un style architectural et RESTful est un terme utilisé pour décrire une application ou un service web qui respecte les principes de REST. Une API RESTful est considérée comme une implémentation correcte et conforme de l'architecture REST.

idempotence C'est une conception d'API REST. Cela concerne le comportement d'une requête lorsqu'elle est appelée plusieurs fois. Une opération est idempotente si la répéter plusieurs fois produit le même effet que de la faire une seule fois.

Pourquoi c'est important ?

- Pour garantir la fiabilité, surtout en cas de pannes réseau ou de retry automatique. - Les clients (navigateurs, passerelles, load balancers, etc.) peuvent renvoyer une requête sans craindre d'effets secondaires inattendus.

Les methodes GET, PUT, DELETE sont idempotentes.

Mongo

Aggregation définit une série d'opérations à travers de collection passée(db.collection.aggregate())

Les opérateurs d'agrégation mongo

$match: L'opérateur $match sert pour filtrer les documents d'une collection. Il est souvent le premier opérateur dans un pipeline d'aggregation mongo. Même si parfois, il arrive de le trouver en seconde position derrière l'opérateur

$project, MongoDB sait identifier tous les champs présents dans le $match suivant et qui sont inexistants avant l'étape $project. Il effectue ensuite sous le capot un $match avant le $project, afin de limiter le nombre de documents à traiter.Après l'étape du $match, il y a normalement moins de documents qu'avant cette étape.

$project: L'opérateur $project a pour but de remodeler chaque document dans la collection. Il prend en paramètre les spécifications que vous voulez mettre en place pour ce nouveau format de document attendu en sortie.

$group: Comme son nom l'indique, l'opérateur d'aggregation mongo $group sert à regrouper les documents et faire des opérations d'accumulation sur les documents.

$unwind: L'opérateur $unwind est le seul opérateur qui va retourner plus de documents à la sortie qu'il n'en a pris à l'entrée. $unwind sert à aplatir les objets contenant un tableau en les dédoublants.

$lookup: L'opérateur $lookup est l'équivalent d'un LEFT OUTER JOIN en SQL. Effectue une jointure à une collection non partagée dans la même base de données pour filtrer les documents

$sort: L'opérateur $sort a pour but de trier les documents en fonction d'une ou plusieurs propriétés de l'objet. Il peut les trier dans l'ordre croissant ou décroissant. Pour bénéficier des meilleures performances de MongoDB, il est fortement conseillé d'utiliser les index sort pour faciliter les requêtes de tri.

$skip et $limit sont des frontières, permettant d'éliminer respectivement les N premiers et les N derniers documents du pipeline.

explain() permet d'évaluer la performance de cette requête.

populate(), qui vous permet de référencer des documents dans d'autres collections.

Mongo Vs CouchDB

Stockage CouchDB utilise JSON pour le stockage des données, CouchDB n'a pas ses données et ses relations dans des tables. MongoDB utilise BSON. Gagnant - CouchDB en tant que JSON est plus efficace et plus rapide car il nécessite moins de temps de calcul et la taille des données est plus petite. Réplication - CouchDB, il permet la réplication maître-maître et maître-esclave. Cela garantit que les données peuvent être consultées à différents endroits sans le moindre retard.D'autre part, MongoDB propose uniquement la réplication maître-esclave. La configuration de la réplication maître-maître est compliquée dans MongoDB et les risques de conflits sont plus importants.

RabbitMq Vs Kafka

https://blog.ippon.fr/2018/03/27/comparatif-rabbitmq-kafka/

Optimization SQL

https://sql.sh/optimisation https://blog.developpez.com/sqlpro/p9816/langage-sql-norme/tout_sur_l_index

Rebase

Toute la branche feature sera ainsi déplacée sur la pointe de la branche master, et tous les nouveaux commits seront intégrés à master. le rebase consiste à réécrire l'historique du projet en créant de nouveaux commits pour chaque commit de la branche d'origine.(ou historique linéaire)

Merge

Un nouveau « commit de merge » est créé dans la branche feature. Il relie les historiques des deux branches.

Rest Vs GraphQL

Une API REST est un concept architectural pour les logiciels basés sur le réseau. GraphQL, en revanche, est un langage de requête, une spécification et un ensemble d'outils qui fonctionnent sur un seul point de terminaison à l'aide de HTTP. Querying Multiple Endpoints OverFetching UnderFetching and n+1 Request Problem

GraphQL résout les problèmes de surextraction et de sous-extraction en permettant au

client de ne demander que les données nécessaires; Puisque le client a plus de liberté

dans les données récupérées, le développement est beaucoup plus rapide avec GraphQL que ce qu'il serait avec REST.

Avec REST, il est plus facile de gérer des requêtes complexes. GraphQL ne renvoie que des points de données spécifiques, il peut donc être plus difficile pour les développeurs de créer des requêtes personnalisées à partir de grands ensembles de données. ... Les réponses des services Web RESTful qui récupèrent des données peuvent généralement être mises en cache plus facilement (en raison du moins de paramètres) que les requêtes GraphQL. GraphQL est plus rapide que REST car comme vous pouvez sélectionner les champs que vous souhaitez interroger, la requête sera toujours la plus petite possible.

Inversion de dépendances(DIP) Vs Injection de dépendance(DI)

Inversion de dépendances (Dependency Inversion Principle, DIP): C'est un principe de conception logicielle qui préconise de dépendre d'abstractions plutôt que de dépendre directement de classes concrètes. L'idée est de réduire le couplage entre les différentes parties d'un système pour rendre celui-ci plus flexible et plus facile à maintenir. Selon ce principe, les modules de haut niveau ne doivent pas dépendre des modules de bas niveau, mais les deux devraient dépendre d'abstractions. De même, les abstractions ne doivent pas dépendre des détails, mais les détails (les implémentations concrètes) doivent dépendre des abstractions. Injection de dépendance (Dependency Injection, DI): C'est une technique de conception logicielle qui implémente l'inversion de dépendance dans un système logiciel. Elle permet de fournir à un objet ses dépendances de l'extérieur plutôt que de les créer lui-même. L'injection de dépendance peut être réalisée de plusieurs façons, telles que par le constructeur, par un setter ou par une interface. Cette technique permet de rendre les classes plus testables, plus modulaires et plus réutilisables, car elles ne sont pas liées à des implémentations spécifiques mais peuvent utiliser n'importe quelle implémentation de l'abstraction requise. En résumé, l'inversion de dépendances est un principe de conception qui guide la structuration des dépendances au sein d'un système, tandis que l'injection de dépendance est une méthode concrète pour réaliser ce principe en fournissant les dépendances à un objet de l'extérieur. L'injection de dépendance est donc une façon d'appliquer le principe d'inversion de dépendances.

Web Vs Internet Vs Extranet

Internet : c'est un réseau qui connecte des millions de réseaux ou c'est le réseau global, permettant la communication et le partage de données. Il inclut l'ensemble des infrastructures physiques (câbles, serveurs, etc.) et les protocoles de communication qui permettent aux appareils de se connecter entre eux. Web : Le web (ou World Wide Web) est un service spécifique qui fonctionne sur internet. Il s'agit de l'ensemble des pages web et sites accessibles via des navigateurs. Les pages web sont connectées entre elles par des hyperliens et sont souvent formatées en HTML. Extranet c'est un réseau qui utilise l'internet (comme les protocoles et le web) pour permettre un accès sécurisé et limité à certaines informations d'une entreprise pour des utilisateurs externes, comme des partenaires ou des clients. L'objectif de l'extranet est de faciliter la collaboration entre l'entreprise et des entités externes en partageant des informations ou des services spécifiques.

Node.js non-bloquant

Node.js utilise une boucle d'événements pour gérer les opérations asynchrones, qui permet à Node.js de gérer de nombreuses opérations en parallèle sans avoir à attendre la fin de chacune avant de commencer la suivante. Si on bloque le traitement, on bloque aussi cette boucle d'événements, ce qui empêche Node.js de traiter d'autres requêtes et diminue les performances. Comment bloquer un traitement dans Node.js Même si ce n'est pas recommandé, il existe des façons de bloquer un traitement dans Node.js : Boucles synchrones ou calculs intensifs :Si on exécute une boucle while qui prend beaucoup de temps ou un calcul complexe de manière synchrone, cela bloquera l'exécution. Utilisation de fonctions de synchronisation pour le système de fichiers : Node.js propose des versions synchrones pour certaines fonctions (par exemple, fs.readFileSync), qui vont bloquer l'exécution jusqu'à ce que le traitement soit terminé.

Module

Un module est un fichier JavaScript ou TypeScript qui contient du code réutilisable. Node.js suit le système de modules CommonJS (require et module.exports), bien qu'il supporte aussi les modules ECMAScript (import et export) dans les versions modernes.

process.nextTick() Vs setImmediate()

process.nextTick() place une tâche dans la microtask queue et l'exécute avant la prochaine phase de la boucle d'événements,

setImmediate() place la tâche dans la phase "check" de la boucle d'événements. En général, process.nextTick() a une priorité plus élevée.

import Vs required

require() appartient à l'ancien système CommonJS, utilisé par défaut dans Node.js depuis toujours.

import fait partie du nouveau standard ES Modules (ESM), utilisé dans JavaScript moderne.

require() fonctionne immédiatement et de manière synchrone, sans configuration particulière. En revanche, pour utiliser import, tu dois soit nommer tes fichiers avec l'extension .mjs, soit ajouter "type": "module" dans ton package.json.

import est plus moderne, plus puissant (supporte l'import dynamique), et recommandé pour

les nouveaux projets. require reste utile dans les anciens projets ou si tu veux garder la compatibilité avec CommonJS.

module.exports Vs exports

module.exports est l'objet qui sera renvoyé lorsque le module est require. exports est une référence vers module.exports. Si exports est réassigné, module.exports ne sera pas affecté, mais on peut toujours ajouter des propriétés à exports qui seront exportées.

Streams

Les streams sont des objets qui permettent de lire ou d'écrire des données de manière continue et asynchrone. Ils sont utiles pour traiter de grandes quantités de données (fichiers, flux réseau) sans tout charger en mémoire. Node.js propose quatre types de streams : Readable, Writable, Duplex et Transform.

Readable Vs Writable Vs Duplex

Readable : Flux uniquement en lecture (ex. lecture d'un fichier). Writable : Flux uniquement en écriture (ex. écriture dans un fichier). Duplex : Flux bidirectionnel, à la fois en lecture et en écriture (ex. socket réseau). Transform : Flux bidirectionnel qui modifie les données lors du transfert (ex. compression de données).

Http

HTTP (HyperText Transfer Protocol) est un protocole de communication utilisé pour échanger des informations sur le web. C'est le protocole, permettant à un client (comme un navigateur web) de demander des ressources à un serveur (comme une page web, une image ou une API). Comment fonctionne HTTP ? HTTP suit un modèle requête-réponse : Le client (exemple : un navigateur web ou une application) envoie une requête HTTP au serveur. Le serveur traite la requête et envoie une réponse HTTP.

Requête HTTP : La requête contient plusieurs parties : Méthode HTTP : Indique l'action demandée. Exemples : GET : Récupérer une ressource (comme une page ou une image). POST : Envoyer des données au serveur (comme un formulaire ou une API). PUT : Mettre à jour ou remplacer une ressource. DELETE : Supprimer une ressource. URL (Uniform Resource Locator) : Spécifie la ressource demandée. En-têtes (Headers) : Métadonnées sur la requête (type de contenu, authentification, etc.). Corps (Body) : Contenu de la requête, utilisé surtout avec POST ou PUT.

Https

HTTPS (HyperText Transfer Protocol Secure) est une version sécurisée du protocole HTTP. Il ajoute une couche de chiffrement et d'authentification grâce au protocole SSL/TLS (Secure Sockets Layer/Transport Layer Security), garantissant que les données échangées entre le client (navigateur) et le serveur restent confidentielles et intègres.

Comment HTTPS fonctionne ?

HTTPS combine le fonctionnement de HTTP avec une couche de sécurité par SSL/TLS. Le client et le serveur établissent une connexion sécurisée et le certificat SSL/TLS est échangé pour authentifier le serveur. Une clé de session (clé symétrique) est générée et utilisée pour chiffrer les données échangées.: Toutes les communications HTTP normales (requêtes et réponses) se font à travers cette connexion sécurisée.

HTTP 1

Les requêtes et réponses sont envoyées une à la fois sur une connexion TCP. Pour charger plusieurs ressources simultanément, il est nécessaire d'ouvrir plusieurs connexions parallèles (souvent limitées à 6 connexions par domaine dans les navigateurs). Cela peut entraîner une surcharge réseau et un ralentissement.

HTTP 2

Permet le multiplexage, c'est-à-dire plusieurs requêtes et réponses envoyées simultanément sur une seule connexion TCP. Les ressources sont chargées plus rapidement sans nécessité d'ouvrir plusieurs connexions.

HTTP/1.1 :

Les en-têtes HTTP sont envoyés en texte brut à chaque requête. Les mêmes en-têtes sont souvent répétés pour plusieurs requêtes (comme les cookies, les User-Agent, etc.), ce qui augmente inutilement la taille des données. HTTP/2 :

Utilise le protocole HPACK pour compresser les en-têtes. Les en-têtes redondants ne sont pas retransmis, réduisant ainsi la surcharge.

Certificat SSL/TLS ?

Un certificat SSL/TLS est un fichier électronique émis par une autorité de certification (CA, Certificate Authority). Il contient :L'identité du propriétaire (nom de domaine, organisation, etc.).Une clé publique pour établir une connexion sécurisée.Une signature numérique de l'autorité de certification. Fonctionnement : Lorsqu'un client (navigateur) se connecte à un site HTTPS, il demande le certificat SSL/TLS.Le certificat est vérifié par le navigateur pour s'assurer qu'il est valide et qu'il provient d'une autorité de confiance.Une connexion sécurisée est établie si tout est en ordre.

CORS

CORS ou (Cross-Origin Resource Sharing) est un mécanisme de sécurité qui permet aux serveurs de contrôler les requêtes provenant de domaines externes. CORS peut être configuré avec le middleware cors pour Express, en spécifiant les domaines autorisés, les méthodes, et les en-têtes. Cela protège contre les requêtes non autorisées provenant d'autres origines. Il protège les utilisateurs contre les attaques malveillantes comme le Cross-Site Request Forgery (CSRF).

JWT

JWT(JSON WEB TOKEN) est composé de trois parties séparées par des points (.) : header, payload et la signature. Chaque partie est encodée en Base64 pour former un jeton compact.

En-tête (Header) :Contient des métadonnées sur le type de jeton et l'algorithme de signature utilisé pour le sécuriser.

Payload (Charge Utile) : Contient les données réclamées (claims) que le JWT transporte. Il existe trois types de claims(réclamations) : réservées, publiques et privées

Signature : Utilisée pour vérifier que le JWT n'a pas été modifié ou falsifié pendant son transit entre les parties. La signature est calculée à l'aide d'un algorithme headers (par exemple, HMAC SHA-256 ou RSA)

Protection de JWT

Chiffrement des Réclamations :

Pour protéger les données sensibles, les réclamations peuvent être chiffrées avant d'être ajoutées au JWT. Cela assure leur confidentialité même si le JWT est décodé par des parties non autorisées.

Utilisation de Clés Secrètes :

Pour les JWT signés (non chiffrés), une clé secrète partagée est utilisée pour générer et vérifier la signature. Cette clé doit être gardée confidentielle pour empêcher la falsification des JWT.

Expiration et Renouvellement :

Les JWT peuvent inclure une date d'expiration (exp) pour limiter leur durée de validité. Après expiration, ils ne sont plus valides et doivent être renouvelés en générant un nouveau JWT.

Validation Côté Serveur :

Lorsque le serveur reçoit un JWT, il vérifie la signature pour s'assurer que le JWT est authentique et n'a pas été modifié. Il vérifie également les réclamations pour décider de la suite des actions. Utilisation de JWT

Authentification et Autorisation :

JWT est utilisé pour l'authentification des utilisateurs après la connexion initiale. Une fois authentifiés, les utilisateurs peuvent présenter le JWT comme preuve de leur identité pour accéder aux ressources protégées.

APIs et Services Web :

JWT est largement utilisé dans les APIs et les services web il est utilisé comme mécanisme de jeton d'accès sécurisé pour contrôler l'accès aux ressources et aux fonctionnalités.

Garbage collection (GC)

child_process Le module child_process permet de créer et de gérer des processus enfants depuis un programme Node.js. Il offre quatre méthodes principales : exec, execFile, spawn, et fork. Ces méthodes sont utiles pour exécuter des commandes système ou des scripts externes, ou pour créer des processus enfants indépendants pour répartir la charge de travail.

spawn et fork

spawn : Utilisé pour lancer un processus enfant qui exécute une commande spécifique, avec un flux de données continu entre le parent et l'enfant. fork : Conçu spécifiquement pour exécuter des scripts Node.js dans un processus enfant. Il crée une instance de V8 et utilise un canal de communication intégré (IPC) pour envoyer des messages entre le processus parent et l'enfant.

Challenge

Au cours de ma carrière en tant que développeur, j'ai rencontré plusieurs défis techniques et fonctionnels, tels que l'optimisation des performances, la sécurité, la gestion de la scalabilité et la communication entre services. 1. Gestion des performances et optimisation des requêtes

Difficulté : Lors du développement de services, il est fréquent de rencontrer des problèmes de performance, surtout lorsque les requêtes à la base de données ou les calculs côté serveur prennent trop de temps. Les lenteurs peuvent impacter directement l'expérience utilisateur et la charge serveur. Solution : J'ai surmonté ce défi en utilisant des outils de profilage pour identifier les goulets d'étranglement (comme les requêtes lentes ou les traitements trop lourds). J'ai optimisé les requêtes SQL, ajouté des index dans les bases de données, et mis en place un système de cache (Redis) pour les données fréquemment demandées. Cela a permis de réduire le temps de réponse.

2. Problèmes de sécurité et gestion des vulnérabilités

Difficulté : La sécurité est une priorité constante dans le développement, car des vulnérabilités comme les injections SQL, le Cross-Site Scripting (XSS) ou les attaques CSRF peuvent mettre en danger les données des utilisateurs. Solution : Pour répondre à ce défi, j'ai systématiquement appliqué les bonnes pratiques de sécurité : validation des entrées utilisateur, utilisation de requêtes paramétrées pour éviter les injections SQL, mise en place de mesures anti-CSRF et protection contre le XSS. J'ai aussi utilisé des outils d'audit de sécurité, comme OWASP ZAP, pour détecter d'éventuelles failles et les corriger avant le déploiement en production.

3. Scalabilité et montée en charge

Difficulté : Au fur et à mesure que le nombre d'utilisateurs d'une application augmente, le backend doit être capable de gérer une montée en charge sans ralentissements ni plantages. Solution : J'ai implémenté des solutions de scalabilité en optimisant l'infrastructure, comme en répartissant les requêtes avec du load balancing et en utilisant des microservices pour décomposer l'application en composants indépendants. J'ai également utilisé le clustering dans Node.js pour tirer parti des multicœurs et optimiser l'utilisation des ressources. Cela m'a permis de maintenir une performance constante, même sous une forte charge.

4. Synchronisation et gestion de la concurrence

Difficulté : Dans certains cas, lorsque plusieurs services ou utilisateurs accèdent simultanément aux mêmes données, il peut y avoir des conflits ou des erreurs de synchronisation, notamment dans les systèmes de paiement ou les systèmes de réservation. Solution : Pour gérer cela, j'ai utilisé des mécanismes de verrouillage, comme des verrous distribués avec Redis pour assurer l'exclusivité de certaines opérations critiques. J'ai aussi mis en place une gestion des transactions dans la base de données pour garantir que les actions soient complètes ou annulées en cas d'erreur. 5. Débogage des erreurs en production

Difficulté : Les erreurs inattendues en production peuvent être difficiles à diagnostiquer, surtout lorsque le problème ne se produit pas en environnement de développement. Solution : J'ai mis en place des outils de journalisation avancée (comme Winston ou ELK Stack) pour enregistrer les erreurs et les événements importants, ainsi que des outils de monitoring (comme Prometheus et Grafana) pour suivre les métriques en temps réel. Ces outils m'ont permis de détecter et diagnostiquer rapidement les problèmes en production, même à distance, et de prendre les mesures nécessaires.

6. Intégration et communication entre microservices

Difficulté : Dans une architecture microservices, la communication entre services peut devenir complexe, avec des problèmes de latence, de dépendances et de gestion des erreurs. Solution : J'ai implémenté des protocoles de communication robustes comme REST et gRPC pour structurer les échanges entre microservices. J'ai également utilisé des files de messages (comme RabbitMQ ou Kafka) pour gérer les événements et les communications asynchrones. Cela a permis de découpler les services et de garantir la fiabilité et la rapidité des interactions entre eux.

COLLABORATION

D'habitude c'est qu'on a fait et de communiquer et d'aligner entre équipes Lorsqu'une équipe prévoit des modifications majeures dans son service, elle doit communiquer les détails (nouveaux contrats, changements d'API, ou suppressions) aux équipes concernées. Via les outils qu'on utilise interne comme d'outils comme teams, Jira, ou des réunions régulières pour synchroniser les plans de chaque équipe.

Versioning des APIs Pour éviter de casser les services consommateurs, on 'adopte une stratégie de versionnement des APIs. Chaque version majeure de l'API est maintenue jusqu'à ce que les consommateurs aient migré vers la nouvelle version.

Left Vs Right join

La différence entre LEFT JOIN et RIGHT JOIN en SQL repose sur le côté de la table dont on souhaite conserver tous les enregistrements, même s'il n'y a pas de correspondance dans l'autre table.

LEFT JOIN (ou LEFT OUTER JOIN): la commande LEFT JOIN (aussi appelée LEFT OUTER JOIN) est un type de jointure entre 2 tables. Cela permet de lister tous les résultats de la table de gauche (left = gauche) même s'il n'y a pas de correspondance dans la deuxième tables et les valeurs vaudront toutes NULL.

RIGHT JOIN (ou RIGHT OUTER JOIN) est un type de jointure entre 2 tables qui permet de retourner tous les enregistrements de la table de droite (right = droite) même s'il n'y a pas de correspondance avec la table de gauche. S'il y a un enregistrement de la table de droite qui ne trouve pas de correspondance dans la table de gauche, alors les colonnes de la table de gauche auront NULL pour valeur.

Sécurité

1. Authentification et gestion des accès - Stockage sécurisé des mots de passe: - Ne jamais conserver les mots de passe en clair : utiliser un algorithme de hachage robuste (bcrypt, Argon2, PBKDF2) avec un sel unique par utilisateur. - Gérer la politique de mot de passe (longueur minimale, complexité) et prévoir un mécanisme de verrouillage ou de délai après plusieurs tentatives échouées.

- Mécanismes d'authentification - Privilégier les protocoles éprouvés (OAuth 2.0, OpenID Connect, JWT) plutôt que de bricoler son propre système. - Si on utilise des JSON Web Tokens (JWT), assurez-vous de bien :Signer et/ou chiffrer le token (algorithme HS256, RS256, etc.). Ne pas stocker d'informations sensibles en clair dans la payload. Implémenter une durée de vie courte et prévoir un mécanisme de rafraîchissement (refresh tokens).

- Contrôle d'accès (autorisation) - Adopter le principe de least privilege : chaque module ou chaque utilisateur ne doit avoir que les droits strictement nécessaires. - Implémenter correctement les rôles et permissions (RBAC) ou, pour des besoins plus fins, un modèle basé sur les attributs (ABAC). - Toujours vérifier côté serveurs (middleware) la permission d'accès à la ressource demandée, même si le front-end (client) a déjà masqué certaines fonctionnalités.

2. Validation des entrées et prévention des injections - Validation stricte côté back-end - Ne jamais faire confiance aux données venant du client : valider systématiquement tout paramètre (types, formats, longueurs). - Utiliser des bibliothèques de validation (ex. Joi, Yup pour Node.js, ou Symfony Validator en PHP, etc.) pour s'assurer que les données respectent le schéma attendu.

- Protection contre les injections SQL / NoSQL - Toujours utiliser des requêtes paramétrées (prepared statements) ou des ORM/frameworks qui gèrent eux-mêmes l'échappement des données. - Éviter de concaténer manuellement des chaînes pour construire une requête SQL/NoSQL.

- En cas d'utilisation de bases NoSQL (MongoDB, etc.), surveiller les opérateurs spéciaux (par ex. $where) qui peuvent conduire à des injections de

type JavaScript.

- Prévention des XSS (Cross-Site Scripting) au niveau API - Même si le XSS affecte plutôt le front-end, le back-end peut involontairement renvoyer du contenu non filtré. - Échapper ou encoder systématiquement toute donnée renvoyée qui pourra ensuite être affichée dans un navigateur (notamment si vous proposez une API qui retourne du HTML ou du Markdown transformé en HTML). 3. Sécurisation des API et des services internes - Authentification mutuelle / tokens inter-services - Si on a un microservice A qui appelle un microservice B, ne faites pas confiance aveuglément aux appels internes. - Utiliser des certificats d'authentification mutuelle (mTLS) ou bien des tokens internes (JWT signés par un secret/clé privée) pour autoriser uniquement certains services.

- Limitation du taux (rate limiting) et throttling - Implémenter un contrôle de débit global (IP ou utilisateur) pour limiter les tentatives (ex. : max 5 requêtes par seconde par IP pour certaines routes sensibles). - Envisager des outils comme Redis ou API Gateway pour centraliser et tenir à jour les compteurs.

- Cross-Origin Resource Sharing (CORS) - Ne pas autoriser de manière trop large les domaines dans l'en-tête Access-Control-Allow-Origin. - Spécifier des domaines précis ou utiliser des listes blanches plutôt que *, surtout sur des API publiques.

- Protection Anti-CSRF (Cross-Site Request Forgery) - Pour les API REST stateless (authentification par token), le CSRF est moins critique, mais si vous utilisez des cookies pour l'authentification, mettre en place des tokens CSRF et valider qu'ils sont présents côté back-end.

Index SQL

Un index dans une base de données (BDD) est une structure qui permet d'améliorer la rapidité de recherche et d'accès aux données. L'index agit comme un annuaire : au lieu de parcourir toutes les lignes d'une table, la base consulte directement l'index pour trouver l'emplacement exact des données recherchées. Types d'index principaux : - Index simple : Appliqué à une seule colonne. Facilite la recherche rapide des données dans cette colonne spécifique. Exemple : index sur une colonne email.

- Index composite (ou multicolonnes) : Concerne plusieurs colonnes combinées. Utile pour accélérer les requêtes complexes impliquant plusieurs conditions. Exemple : index combiné sur (nom, prénom).

- Index unique : Garantit l'unicité des données dans la colonne concernée. Empêche l'insertion de doublons. Exemple : index unique sur une colonne id.

- Index Clustered (index groupé) : Détermine physiquement l'ordre des lignes dans la table. Une table ne peut avoir qu'un seul index clustered (souvent la clé primaire).

- Index Non-Clustered (non groupé) : Stocke un pointeur vers la ligne de données. Une table peut avoir plusieurs index non-clustered.

Dichotomique

La recherche dichotomique est une méthode pour trouver un élément dans un tableau trié, en le divisant en deux à chaque étape. Sa complexité est faible (logarithmique), donc elle est très rapide même pour de grands tableaux: O(log₂ n) La complexité est O(log₂ n)": Cela veut dire que le nombre d'étapes nécessaires augmente très lentement, même si la taille de la liste augmente beaucoup. Pourquoi "log₂" ? Le logarithme base 2 (noté log₂) répond à la question : "Combien de fois faut-il diviser par 2 pour arriver à 1 ?" Exemples : log₂(8) = 3 car 8 ÷ 2 ÷ 2 ÷ 2 = 1 (3 divisions) log₂(1024) = 10 car 1024 ÷ 2 dix fois = 1 Donc, si on a une liste de 1024 éléments, seulement 10 comparaisons suffisent pour trouver une valeur avec la recherche dichotomique !

Séquentielle (ou linéaire)

On parcourt tous les éléments un par un, depuis le début jusqu'à la fin, pour trouver la valeur cherchée. Exemple : Liste = [7, 2, 9, 4, 5] Valeur cherchée = 4 On regarde : 7 ? non, 2 ? non, 9 ? non, 4 ? oui Complexité : Temps d'exécution : O(n) (n = nombre d'éléments) Pas très rapide si la liste est longue, mais fonctionne toujours, même sans tri.

Haute disponibilité

La haute disponibilité désigne la capacité d'un système informatique à rester accessible et opérationnel en continu, même en cas de défaillance matérielle ou logicielle. L'objectif est de minimiser les interruptions de service.

Principes clés de la haute disponibilité

1. Redondance Avoir plusieurs instances des composants critiques (serveurs, bases de données, etc.) pour qu'en cas de panne, un autre prenne automatiquement le relais.

2. Tolérance aux pannes Le système continue à fonctionner même si une partie échoue.

3. Surveillance et détection de pannes Des outils supervisent le système en temps réel pour détecter les problèmes et relancer automatiquement les services si besoin.

4. Load balancing (répartition de charge) Permet de distribuer les requêtes entre plusieurs instances afin d'éviter la surcharge et de rester disponible même si une instance tombe.

Scalabilité verticale

Augmenter la puissance de chaque instance de service (plus de CPU, RAM, stockage, etc.). Exemple : Tu fais tourner ton microservice sur un serveur avec 4 CPU. Pour supporter plus de charge, tu passes à un serveur avec 8 CPU. Avantages : - Plus simple à mettre en œuvre (pas besoin de changer l'architecture). - Moins de complexité réseau (tout reste sur une seule machine).

Inconvénients : - Limite physique du matériel atteinte rapidement. - Moins de résilience (si le serveur tombe, tout tombe). - Moins adapté à des systèmes distribués comme les microservices.

Scalabilité horizontale

Ajouter plus d'instances du même service et répartir la charge entre elles (ex. via un load balancer). Exemple : Tu as 1 instance de ton microservice, tu en lances 4 de plus. Toutes gèrent une partie des requêtes. Avantages : - Meilleure tolérance aux pannes (si une instance tombe, les autres prennent le relais). - Théoriquement illimité (tant que l'infrastructure le permet). - Mieux adapté aux architectures cloud, Kubernetes, etc.

Inconvénients : - Plus complexe à gérer (synchronisation, orchestration, mise en cache, etc.). - Besoin d'outils pour le load balancing et le monitoring. Dans le contexte microservices, la scalabilité horizontale est préférée, car elle s'aligne mieux avec une architecture distribuée et permet une meilleure flexibilité et résilience.

Suivi de la performance applicative et la mise en place d'optimisations

Objectifs du suivi de performance : - Identifier les goulots d'étranglement (bottlenecks) - Réduire les temps de réponse des APIs - Optimiser la consommation mémoire et CPU - Améliorer la scalabilité du système - Réduire le temps d'exécution des traitements (batchs, scripts, etc.) Étapes clés du suivi de performance : 1. Mise en place d'outils de monitoring

Utilisation d'outils pour observer l'état en temps réel de l'application :

- APM (Application Performance Monitoring) : Exemples : Datadog, New Relic, Dynatrace, AppDynamics. Mesures : temps de réponse, erreurs, temps CPU, mémoire, throughput, etc. - Prometheus + Grafana : pour des dashboards personnalisés - Node.js built-in tools : process.memoryUsage(), process.cpuUsage() - Logs centralisés : via ELK stack (Elasticsearch, Logstash, Kibana) ou Grafana Loki 2. Analyse des métriques critiques - Temps de réponse des endpoints API (latence) - Taux d'erreur HTTP (500, 400, etc.) - Utilisation CPU et mémoire Nombre de requêtes par seconde (RPS) - Temps de requête base de données Mise en place d'optimisations concrètes 1. Optimisations de code - Utiliser des fonctions asynchrones non bloquantes (async/await, Promise.all) - Éviter les boucles inutiles, les opérations coûteuses côté serveur - Utilisation du lazy loading pour les modules lourds

2. Optimisation des requêtes SQL (avec TypeORM ou autres) - Réduction des requêtes imbriquées ou N+1

- Mise en place d'index dans la base PostgreSQL - Utilisation de transactions pour limiter les erreurs - Caching de résultats de requêtes lourdes (ex. : avec Redis)

3. Mise en cache - Mise en place de cache applicatif via Redis ou Node-cache - Caching des réponses HTTP pour certaines routes (avec etag, cache-control, etc.) - Cache des tokens, configurations, résultats de requêtes fréquentes

4. Gestion de la charge (scalabilité) - Utilisation de Cluster mode (avec pm2 ou cluster de Node.js) pour utiliser tous les cœurs CPU - Mise en place de load balancing si l'application est répartie sur plusieurs instances - Optimisation des performances I/O (fichiers, appels externes) 5. Tests de performance - Utilisation d'outils comme :Artillery, k6, JMeter, Locust - Simulation de charge (stress test, spike test) - Identification des endpoints les plus lents et des comportements dégradés

WAF

WAF (Web Application Firewall) : Un pare-feu applicatif web (WAF) est un dispositif de sécurité (logiciel ou matériel) placé devant une application web pour filtrer, surveiller et bloquer le trafic HTTP malveillant. Rôle : Il protège les applications web contre les attaques courantes comme : - Injection SQL - Cross-Site Scripting (XSS) - Cross-Site Request Forgery (CSRF) - Exploitation de vulnérabilités connues

Fonctionnement : Le WAF analyse les requêtes HTTP/HTTPS entrantes et sortantes selon des règles de sécurité (signatures, comportement, listes noires/blanches).

OWASP (Open Web Application Security Project)

L'OWASP est une organisation internationale à but non lucratif qui œuvre pour améliorer la sécurité des applications web.

Rôle principal :

- Publier des guides, outils et ressources pour les développeurs et les entreprises. - Le plus connu est le OWASP Top 10, une liste des 10 principales failles de sécurité web (mise à jour régulièrement).

Exemples de failles OWASP Top 10 :

- A01: Injection

- A02: Mauvaise gestion de l'authentification - A03: Exposition de données sensibles

- A05: Mauvaise configuration de sécurité - A04: Insecure Design (Conception non sécurisée) : Exemple : Une application de e-commerce qui ne limite pas le nombre de tentatives de paiement, permettant des attaques par force brute.

  • A05: Security Misconfiguration (Mauvaise configuration de sécurité) Exemple : Un serveur web expose les pages d'administration par défaut (/phpmyadmin) sans être protégées.
  • A06: Vulnerable and Outdated Components (Composants vulnérables ou obsolètes): Exemple : Utiliser une ancienne version de Struts ou WordPress avec des failles connues et non corrigées.
  • A07: Identification and Authentication Failures (Défaillances d'authentification): Exemple : Mots de passe faibles acceptés (123456) ou absence de double authentification (MFA).
  • A08: Software and Data Integrity Failures (Défaillances d'intégrité logicielle et des données) Exemple : Téléchargement automatique d'une mise à jour sans vérifier la signature possibilité d'injecter un malware.
  • A09: Security Logging and Monitoring Failures (Défaillances de journalisation et de surveillance): Exemple : Aucune alerte n'est générée après 1000 tentatives de connexion échouées attaque par brute force indétectée.
  • A10: Server-Side Request Forgery (SSRF falsification de requêtes côté serveur): Exemple : Une application permet de télécharger une image depuis une URL fournie par l'utilisateur, mais sans validation un attaquant peut accéder à http://localhost:8080/admin.