Valuable insights
1.Node.js : Environnement d'exécution côté serveur: Node.js permet d'exécuter du code JavaScript en dehors du navigateur, utilisant le moteur V8 de Chrome pour des performances élevées grâce à son architecture non bloquante.
2.Express.js pour la construction des routes API: Express.js sert de cadre pour définir des points de terminaison (routes) en utilisant des méthodes HTTP spécifiques, gérant les requêtes entrantes et les réponses sortantes.
3.Gestion des données avec Sequelize et MariaDB: L'ORM Sequelize abstrait les interactions avec la base de données SQL MariaDB, permettant de manipuler les données via des modèles JavaScript au lieu d'écrire directement des requêtes SQL.
4.Importance des Middlewares Express: Les middlewares permettent de factoriser des traitements communs (logging, sécurité, parsing) qui s'exécutent séquentiellement sur les requêtes et réponses, comme illustré avec Morgan.
5.Structuration des réponses API avec JSON: Les réponses API doivent être formatées en JSON, incluant un code de statut HTTP approprié. Des modules utilitaires personnalisés aident à structurer ces réponses de manière cohérente.
6.Validation des données avec les validateurs Sequelize: Des validateurs personnalisés et natifs (comme isInt, notNull) sont définis dans les modèles Sequelize pour garantir la cohérence des données avant même leur envoi à la base de données.
7.Requêtes avancées via Query Parameters: Les paramètres de requête (query params) affinés dans l'URL permettent de filtrer, trier et limiter les résultats renvoyés par les points de terminaison, améliorant l'élaboration des requêtes GET.
8.Sécurisation de l'API avec JWT et Bcrypt: L'authentification nécessite le chiffrement des mots de passe via bcrypt et l'utilisation de jetons JWT pour sécuriser les échanges de données entre le client et les routes protégées de l'API.
9.Déploiement en production sur Heroku: Le déploiement requiert l'usage de variables d'environnement pour adapter les configurations locales (ports, identifiants DB) et l'utilisation de l'outil Heroku CLI pour gérer le processus de mise en ligne.
10.Gestion des accès externes via CORS: La politique CORS (Cross-Origin Resource Sharing) doit être implémentée via un middleware Express pour autoriser les applications frontend (React, Angular, Vue) à communiquer avec l'API hébergée sur un domaine différent.
Introduction au Tutoriel Node.js
Une formation complète de plus de sept heures est présentée, visant à amener les participants du niveau débutant à la capacité de déployer une API REST fonctionnelle avec une base de données sur Internet. La maîtrise préalable du langage JavaScript constitue un prérequis essentiel pour suivre ce parcours de développement backend.
Objectifs et prérequis du cours
L'instructeur, Simon, partage son expérience de formateur auprès d'ingénieurs sur les technologies JavaScript modernes. L'objectif principal de cette chaîne est de démystifier le développement d'applications web complètes, considérées comme des logiciels fonctionnant directement dans un navigateur. Une partie du contenu avancé est disponible moyennant un lien en description.
Soyez persévérant ne lâchez rien et ça devrait bien se passer.
Définition de Node.js et des API REST
Node.js est défini comme un environnement permettant l'exécution de code JavaScript côté serveur, par opposition à l'exécution habituelle dans le navigateur. Pour clarifier cette notion, il est nécessaire de comprendre ce qu'est un environnement d'exécution en informatique : le lieu où le code devient un produit utilisable.
Le rôle des moteurs JavaScript dans les navigateurs
Chaque navigateur intègre son propre moteur JavaScript invisible pour interpréter le code. Par exemple, Internet Explorer utilise Chakra, Mozilla utilise SpiderMonkey, et Chrome utilise le moteur V8. Cette disparité explique pourquoi le même code JavaScript peut fonctionner différemment selon le navigateur utilisé par l'utilisateur final.
- Chakra (Internet Explorer)
- SpiderMonkey (Mozilla)
- V8 (Chrome)
L'intégration de V8 dans Node.js
Node.js, créé pour exécuter JavaScript hors navigateur, intègre directement le moteur JavaScript le plus performant de l'époque : V8, rendu open source par Google. Ainsi, l'environnement serveur (Node.js + V8) et l'environnement web (Navigateur + Moteur spécifique) partagent le même langage d'exécution, bien que les consoles d'exécution diffèrent.
Avantages majeurs de l'utilisation de Node.js
Le succès de Node.js repose sur plusieurs atouts par rapport aux environnements serveurs traditionnels comme PHP ou Java. L'utilisation du JavaScript, langage le plus répandu, unifie les compétences de l'équipe, simplifiant les ressources et la communication entre développeurs frontend et backend. De plus, Node.js est extrêmement rapide grâce au moteur V8 et à son architecture non bloquante, le rendant idéal pour les applications temps réel.
- Unification du langage (JavaScript) pour le client et le serveur.
- Rapidité d'exécution grâce au moteur V8 et architecture non bloquante.
- Légèreté, nécessitant uniquement l'ajout de modules spécifiques.
- Écosystème large via NPM (Node Package Manager).
- Popularité assurant une grande communauté d'aide.
Installation et Premiers Pas avec Node.js
L'installation de Node.js nécessite de choisir entre la version LTS (Long Term Support), recommandée pour sa stabilité et sa viabilité à long terme, et la version actuelle, qui intègre les dernières fonctionnalités mais peut encore subir des ajustements. Il est conseillé de privilégier la version LTS pour le démarrage d'un nouveau projet.
Vérification de l'installation et outils
L'installation de Node.js inclut automatiquement NPM (Node Package Manager), essentiel pour gérer les dépendances. Il est impératif de vérifier que Node.js et NPM sont correctement installés en exécutant respectivement les commandes
- Visual Studio Code (Gratuit et complet)
- WebStorm (Payant, licence étudiante disponible)
- Sublime Text (Gratuit, configuration plus manuelle)
Initialisation du premier projet Node.js
Le démarrage d'un projet commence par la création d'un dossier et d'un fichier d'entrée, souvent nommé
Le fichier package.json a au moins deux missions principales : donner une description rapide à votre projet et lister toutes les dépendances de votre application.
L'exécution du script initial se fait avec la commande
Installation de la première dépendance : Express
Express.js, le framework le plus populaire pour créer des API REST avec Node.js, est installé via NPM. L'option
Découvrir les Routes avec Express.js
La construction d'un point de terminaison dans une API REST avec Express repose sur l'équation :
Définition d'un point de terminaison GET basique
Le code minimal pour définir une route GET sur la racine (
Le terme GET que l'on retrouve au début de la ligne va prendre en paramètre deux éléments : premièrement le chemin de la requête, deuxièmement une fonction qui fournit une réponse au client.
Amélioration du développement avec Nodemon
Le développement est ralenti par la nécessité de stopper et relancer la commande
- Dependencies (Express) : Nécessaires pour l'exécution en production.
- DevDependencies (Nodemon) : Uniquement nécessaires durant le développement local.
Les Réponses JSON dans une API REST
Une réponse HTTP valide dans une API REST doit comporter plusieurs éléments cruciaux : les données demandées, le format de ces données (idéalement JSON), le type MIME (spécifiant
Utilisation de la méthode res.json()
Pour garantir que les données sont envoyées au format JSON avec le type MIME correct, la méthode
On ne pourra jamais vous reprocher d'être trop précis concernant la qualité et la fiabilité des réponses renvoyées par l'API.
Structuration des réponses avec un module utilitaire
Afin d'améliorer la lisibilité et la cohérence des réponses, un module utilitaire est créé pour encapsuler les données dans une structure JSON standardisée, incluant un message explicatif en plus des données brutes. L'utilisation des fonctionnalités ES6 permet de rendre ce module extrêmement concis, fusionnant propriétés et valeurs si elles portent le même nom.
- Utilisation de l'affectation déstructurée pour importer uniquement la fonction nécessaire (ex:
const { success } = require('./lpr') ). - Raccourci d'objet pour exporter la fonction directement sans variable intermédiaire.
Les Middlewares d'Express
Les middlewares sont des fonctions JavaScript capables d'interagir avec les requêtes entrantes et sortantes de l'API REST avant qu'elles n'atteignent les points de terminaison finaux. Ils reçoivent les objets
Catégories et cas d'utilisation des Middlewares
- Application-level (via
app.use ) : Pour le logging des requêtes entrantes. - Router-level : Pour organiser des sous-ensembles de routes.
- Error-handling : Nécessite quatre arguments pour intercepter les erreurs.
- Built-in (ex:
express.static ) : Pour servir des fichiers statiques. - Third-party : Dépendances externes à installer (ex: Morgan).
Un middleware personnalisé est créé pour logger l'URL de chaque requête entrante dans le terminal du serveur. Bien que fonctionnel, il est préférable d'utiliser des solutions existantes et éprouvées. Le paquet Morgan est installé comme dépendance de développement pour fournir un logging plus abouti et coloré des requêtes.
Chaînage et ordre d'exécution
La puissance des middlewares réside dans leur capacité à être combinés en une chaîne de traitement. L'ordre d'appel via
- Installation du middleware Serve-favicon.
- L'appel à
app.use() pour favicon est placé avant Morgan, car il doit s'exécuter tôt pour ajouter l'icône à chaque réponse.
Construction d'une API REST Complète (CRUD)
L'objectif est d'implémenter les opérations CRUD (Create, Read, Update, Delete) sur les ressources Pokémon. L'ajout d'un nouveau Pokémon nécessite une requête HTTP de type POST sur l'URL de la collection (
Gestion des identifiants uniques pour les ressources
Un défi majeur lors de l'implémentation de POST est la génération d'un identifiant unique. Puisque la base de données n'est pas encore impliquée, une méthode utilitaire est développée dans le module lpr.js. Cette méthode calcule le maximum des IDs existants et y ajoute un, garantissant ainsi l'unicité locale.
C'est à la base de données de déterminer les identifiants uniques puisque elle seule a accès à l'ensemble des pokémons existants.
Contraintes du format de données en POST
Lors de l'envoi de données POST via des outils comme Insomnia, le corps de la requête est reçu comme une chaîne de caractères brute par HTTP. Pour accéder aux propriétés des données, il est impératif de parser cette chaîne en objet JavaScript. Le middleware body-parser (ou l'équivalent Express) est introduit pour gérer cette conversion automatiquement.
- GET /api/pokemons : Récupérer la liste ou un Pokémon spécifique (avec ID en paramètre d'URL).
- POST /api/pokemons : Ajouter un nouveau Pokémon.
- PUT /api/pokemons/:id : Remplacer entièrement une ressource existante.
- DELETE /api/pokemons/:id : Supprimer une ressource.
Introduction aux erreurs métier et validation
La gestion des erreurs est divisée entre erreurs techniques (liées à la connexion DB, 500) et erreurs métier (spécifiques aux règles de l'application, comme un nombre de types invalide). Pour éviter d'interroger inutilement la base de données, les validateurs JavaScript sont préférés aux contraintes SQL pour les vérifications côté application.
La Base de Données SQL et l'ORM
L'intégration d'une base de données est nécessaire pour la persistance des données. L'installation de XAMPP fournit un environnement local incluant le serveur web Apache, la base de données MariaDB et l'interface de visualisation phpMyAdmin. Cependant, l'interaction directe avec phpMyAdmin est déconseillée pour les développeurs backend.
L'abstraction fournie par les ORM
L'ORM (Object Relational Mapping) est une technique puissante qui masque la complexité du langage de requête SQL. Il permet d'interagir avec la base de données en manipulant des objets JavaScript, offrant une abstraction qui facilite le changement de système de base de données (MariaDB, PostgreSQL, etc.) sans réécrire les requêtes.
- Interaction avec la base de données via des méthodes JavaScript familières.
- Abstraction du langage SQL et des concepts de tables/colonnes.
- Facilité de migration entre différents systèmes SQL.
Choix de Sequelize et installation des Drivers
Sequelize est sélectionné comme ORM pour Node.js, car il est entièrement basé sur les Promesses JavaScript, gérant efficacement les traitements asynchrones. Pour que Sequelize puisse communiquer avec MariaDB (installé via XAMPP), le driver spécifique mysql2 doit être installé comme dépendance du projet.
La configuration de la connexion se fait dans
Relier votre API Rest et la base de données
Le concept fondamental de Sequelize est le Modèle, qui est une abstraction JavaScript représentant une table dans la base de données sous-jacente. Un modèle Pokémon est déclaré dans
Définition du modèle Pokémon avec Sequelize
La méthode
Il est vraiment la bienvenue d'utiliser un ORM, car on peut interagir avec notre base de données sans connaître le langage de requête SQL.
Refactorisation de l'architecture du code
Le fichier
- findAll : Pour récupérer tous les enregistrements (remplace la liste statique).
- findByPk : Pour récupérer une ressource par sa clé primaire (ID).
- create : Pour insérer une nouvelle ligne dans la table.
- update / destroy : Pour modifier ou supprimer des enregistrements.
Traiter les Erreurs Courantes de l'API
La gestion des erreurs est cruciale pour la qualité d'une API REST. Les erreurs sont divisées entre erreurs de programmation (que l'on corrige localement) et erreurs opérationnelles (survenant lors de l'interaction client, comme des données invalides ou une ressource inexistante). Il est vital de retourner des codes de statut HTTP précis (400, 404, 500) plutôt que des erreurs génériques 500.
Gestion de l'erreur 404 pour les routes inconnues
Express gère nativement les routes non trouvées avec un code 404, mais ce comportement par défaut manque de personnalisation. Un middleware spécifique doit être ajouté après toutes les déclarations de routes dans
Gestion des erreurs asynchrones dans les requêtes Sequelize
Les appels à la base de données via Sequelize retournent des promesses. Il est nécessaire d'ajouter un bloc
L'erreur 404 indique que la page demandée n'a pas été trouvé. C'est ce fameux code 404 qu'on peut retrouver un peu partout.
Dans les points de terminaison
Créer des Validateurs Personnalisés
Pour appliquer des règles métier spécifiques qui ne sont pas couvertes par les contraintes SQL (comme vérifier la structure des types Pokémon), les validateurs personnalisés de Sequelize sont utilisés. Ces validateurs s'exécutent côté JavaScript, évitant des requêtes inutiles à la base de données et offrant une réponse plus rapide au client.
Définition des validateurs natifs et personnalisés
Des validateurs intégrés comme
- Name :
notEmpty etnotNull . - HP (Points de vie) :
isInt etnotNull , avec des validateursmin etmax (0 à 999). - Damage (Dégâts) :
isInt etnotNull , avec des validateursmin (0) etmax (99). - Picture :
isUrl etnotNull .
Gestion des erreurs de validation dans les routes
Lorsque Sequelize lève une erreur de validation, il retourne une erreur technique 500 par défaut. Pour améliorer l'expérience utilisateur, il faut intercepter cette erreur dans le bloc
Concernant les contraintes SQL (comme l'unicité du nom d'utilisateur), elles sont définies directement dans le modèle User via Sequelize. Si cette contrainte est enfreinte, Sequelize lève une erreur spécifique (
Requêtes Avancées et Filtrage des Données
Pour offrir une API plus élaborée, il est nécessaire de dépasser la simple récupération de la liste complète ou d'une ressource par ID. Les paramètres de requête (query parameters), ajoutés à la fin de l'URL (ex:
Filtrage et recherche avec l'opérateur LIKE
La recherche exacte par nom est trop restrictive. L'opérateur
L'opérateur LIKE de Sequelize permet de rechercher à tel ou tel endroit dans une des propriétés de nos modèles.
Limitation et Comptage des Résultats (Pagination)
Pour optimiser les ressources, la fonctionnalité de limitation des résultats est implémentée en passant une option
Le tri alphabétique croissant des résultats est ajouté en utilisant l'option
Authentification avec JWT
La sécurisation de l'API passe par deux exigences : le chiffrement des mots de passe et la sécurisation des échanges de données après authentification. Le standard JSON Web Token (JWT) est adopté pour gérer l'état d'authentification de manière stateless.
Chiffrement des mots de passe avec Bcrypt
Le module bcrypt est installé pour hacher les mots de passe avant de les stocker en base de données. La méthode
Génération et Vérification des Jetons JWT
Un jeton JWT est généré lors de la connexion réussie, contenant l'ID utilisateur et une date de validité limitée (ex: 24 heures). Ce jeton est créé en utilisant la méthode
- Client envoie identifiant/mot de passe au point de terminaison
/login . - API vérifie les identifiants (via bcrypt.compare) et génère un JWT.
- Client utilise le JWT dans l'en-tête des requêtes subséquentes.
- Middleware auth vérifie la validité du jeton avant d'autoriser l'accès à la ressource.
Le middleware d'authentification est appliqué sélectivement aux routes nécessitant une protection (CRUD Pokémon), tout en laissant la route de connexion (
Déployer votre API Rest en Production
Le déploiement en production implique de rendre l'API accessible sur le web, nécessitant l'hébergement du serveur Node.js et de la base de données sur une plateforme distante. Heroku est choisi pour sa gratuité initiale et son intégration avec les outils de développement modernes.
Préparation locale et outils de déploiement
Le déploiement sur Heroku nécessite l'installation de Git pour le versionnage et de l'utilitaire Heroku CLI. Les variables d'environnement, accessibles via
- Séparer les scripts dans
package.json (dev pour local avec nodemon,start pour prod). - Définir NODE_ENV à
production pour optimiser Express. - Supprimer les dépendances de développement (Morgan, Nodemon) des dépendances de production.
Configuration de la Base de Données en Production
Un add-on Heroku (JawsDB Maria) est ajouté pour provisionner une base de données distante. Le fichier
Le déploiement final s'effectue via les commandes Git et Heroku CLI, initialisant le dépôt et poussant le code vers le serveur distant. Un dernier point de terminaison public (
Bonus : Connecter une Application Web Frontend
L'étape finale consiste à connecter une application frontend (Native JS, React, Angular, Vue) à l'API REST déployée. La communication entre le frontend (hébergé sur une origine) et le backend (API) est soumise à la politique de sécurité des navigateurs, notamment la Same-Origin Policy.
Comprendre et Implémenter CORS
Pour autoriser les requêtes cross-origin, le middleware cors est installé et utilisé dans
L'en-tête Access-Control-Allow-Origin est essentielle à la mise en place de la norme CORS et également la sécurité des ressources.
Exécution des requêtes depuis le Frontend
Les applications frontend doivent suivre un ordre chronologique : 1. Authentification (POST sur
- Native JS (Fetch) : Nécessite une conversion manuelle des réponses en JSON et une gestion des promesses avec
.then() . - React/Vue (Axios) : Les données sont encapsulées dans la propriété
data de la réponse. - Angular (HttpClient) : Utilise la programmation réactive avec des Observables et l'opérateur
switchMap pour gérer l'enchaînement des requêtes.
Useful links
These links were generated based on the content of the video to help you deepen your knowledge about the topics discussed.