Recherche sur le site Web

Comment utiliser Sequelize avec Node.js et MySQL


L'auteur a choisi le Fonds pour un Internet ouvert/liberté d'expression pour recevoir un don dans le cadre du programme Écrire pour des dons.

Introduction

Sequelize est un ORM (Object Relational Mapper) basé sur Node.js qui facilite le travail avec les bases de données MySQL, MariaDB, SQLite, PostgreSQL et bien d'autres. Un Object Relational Mapper exécute des fonctions telles que la gestion des enregistrements de base de données en représentant les données sous forme d'objets. Sequelize dispose d'un mécanisme de migration puissant qui peut transformer les schémas de base de données existants en de nouvelles versions. Dans l'ensemble, Sequelize offre un excellent support pour la synchronisation des bases de données, le chargement avide, les associations, les transactions et les migrations de bases de données tout en réduisant le temps de développement et en empêchant les injections SQL.

Dans ce tutoriel, vous allez installer et configurer Sequelize avec MySQL sur votre environnement de développement local. Ensuite, vous utiliserez Sequelize pour créer des bases de données et des modèles, ainsi que pour effectuer les opérations insert, select et delete. Ensuite, vous créerez des associations Sequelize pour les relations un-à-un, un-à-plusieurs et plusieurs-à-plusieurs. Enfin, vous créerez des requêtes brutes Sequelize pour le remplacement de tableaux et d'objets. En plus de ces bases, le tutoriel couvre aussi la configuration sécurisée des bases de données à l'aide de variables d'environnement, la gestion sécurisée des changements de schéma via les migrations et les meilleures pratiques pour le dépannage et l'optimisation des performances.

Déployez vos applications frontend depuis GitHub en utilisant la plateforme d'applications Linux-Console.net. Laissez Linux-Console.net se charger de la montée en charge de votre application.

Points clés à retenir :

  • Sequelize offre un moyen facile d'interagir avec MySQL en utilisant des modèles plutôt que du SQL brut, ce qui rend les opérations de base de données plus simples et mieux organisées.
  • Stocker les identifiants de base de données dans des variables d'environnement permet de garder les informations sensibles en sécurité et hors du contrôle de version.
  • Utiliser des migrations pour les mises à jour de schéma est plus sûr que de compter sur sequelize.sync(), surtout dans les environnements de production.
  • Sequelize facilite la définition des relations un-à-un, un-à-plusieurs et plusieurs-à-plusieurs entre les modèles.
  • Les requêtes SQL brutes peuvent toujours être utilisées lorsque vous avez besoin de plus de contrôle ou d’une meilleure performance pour des opérations complexes sur la base de données.
  • Les problèmes les plus courants avec Sequelize, tels que les échecs de connexion ou les erreurs de migration, peuvent être résolus en vérifiant les variables d'environnement, en consultant les journaux et en utilisant des outils MySQL comme EXPLAIN et SHOW PROCESSLIST.
  • Les performances peuvent être améliorées en optimisant les requêtes, en ajoutant des index appropriés, en ajustant les pools de connexions et en mettant en cache les données consultées fréquemment.
  • Tester les migrations, ajouter la gestion des erreurs et surveiller les performances de la base de données aident à maintenir les applications Sequelize fiables et prêtes pour la production.

Prérequis

Pour compléter ce tutoriel, vous aurez besoin de :

  • Un environnement de développement fonctionnant avec Node.js. Pour le configurer, suivez le tutoriel Comment installer Node.js et créer un environnement de développement local pour votre système d'exploitation.
  • MySQL installé dans votre environnement de travail. Suivez le tutoriel Comment installer MySQL pour votre environnement, y compris les étapes pour créer un utilisateur.
  • Familiarité avec JavaScript et les requêtes MySQL, que vous pouvez développer avec la série Comment coder en JavaScript et le tutoriel Une introduction aux requêtes en MySQL.
  • Une compréhension de base des concepts de bases de données relationnelles, que vous pouvez trouver dans le tutoriel Comprendre les bases de données relationnelles.

Ce tutoriel a été testé sur la version v24.11.1 de Node.js et la version 11.6.2 de npm.

Installation et configuration de Sequelize

Dans cette étape, vous allez installer Sequelize et créer la connexion à votre base de données MySQL. Pour ce faire, vous commencerez par créer une application Node.js. Ensuite, vous installerez Sequelize, configurerez la base de données MySQL et développerez une application simple.

Installation de Sequelize

Commencez par créer un dossier pour le projet. Dans cet exemple, vous pouvez utiliser hello-world. Une fois le dossier créé, accédez-y en utilisant le terminal :

mkdir hello-world

cd hello-world

Ensuite, créez une application Node.js exemple en utilisant la commande suivante :

npm init

Ensuite, il vous sera demandé de répondre à quelques questions de configuration. Utilisez la sortie suivante pour votre configuration. Appuyez sur ENTRÉE pour utiliser la valeur par défaut affichée et assurez-vous de définir le point d'entrée principal comme server.js. Cela crée une structure de projet facile à maintenir.

La sortie apparaîtra comme suit, ce qui remplira le fichier package.json :

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}

Ensuite, créez un fichier vide server.js à l'intérieur du dossier du projet :

touch server.js

Après avoir suivi les étapes précédentes, la structure finale de vos dossiers ressemblera à ceci :

hello-world/
├─ package.json
├─ server.js

Vous pouvez maintenant installer Sequelize avec la commande suivante :

npm i sequelize

Après ces mises à jour, le fichier package.json ressemble maintenant à ceci :

{
  "name": "hello-world",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "sequelize": "^6.37.7"
  }
}

Dans la section dependencies, vous verrez maintenant une dépendance Sequelize.

Vous avez configuré le projet et installé Sequelize. Ensuite, vous allez créer une base de données d'exemple à laquelle vous connecter.

Créer une base de données d'exemple

Dans le cadre des prérequis, vous avez installé et configuré MySQL, ce qui incluait la création d’un utilisateur. Vous allez maintenant créer une base de données vide.

Pour ce faire, vous devez d'abord vous connecter à votre instance MySQL. Si vous y accédez à distance, vous pouvez utiliser l'outil de votre choix. Si vous utilisez une instance MySQL locale, vous pouvez utiliser la commande suivante, en remplaçant votre_nom_d_utilisateur par votre nom d'utilisateur MySQL :

mysql -u your_username -p

-u est le nom d'utilisateur et l'option -p est utilisée si le compte est protégé par un mot de passe.

Le serveur MySQL vous demandera le mot de passe de votre base de données. Tapez votre mot de passe et appuyez sur ENTRÉE.

Une fois connecté, créez une base de données appelée hello_world_db en utilisant la commande suivante :

CREATE DATABASE hello_world_db;

Pour vérifier si vous avez créé la base de données avec succès, vous pouvez utiliser cette commande :

SHOW DATABASES;

Votre sortie sera similaire à ceci :

+--------------------+
|      Database      |
+--------------------+
| hello_world_db     |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+

Après avoir créé la base de données d’exemple, déconnectez-vous du serveur MySQL :

mysql> QUIT

Maintenant, vous devez installer un pilote manuel pour votre base de données de choix. Comme Sequelize ne fournit que des fonctionnalités ORM, il n’inclut pas de pilotes de base de données intégrés. Par conséquent, vous devrez installer des pilotes selon votre préférence. Pour ce faire, naviguez jusqu’au répertoire du projet à l’aide du terminal et installez le pilote MySQL pour le projet en utilisant la commande suivante :

npm install --save mysql2

Dans ce cas, vous utilisez le pilote pour MySQL.

Remarque : Puisque ce tutoriel utilise MySQL comme base de données, vous utilisez un pilote pour cela. Selon votre base de données, vous pouvez installer le pilote manuellement de la manière suivante :

  • npm install --save pg pg-hstore # Postgres
  • npm install --save mysql2
  • npm install --save mariadb
  • npm install --save sqlite3
  • npm install --save tedious # Microsoft SQL Server

Maintenant que vous avez une base de données d'exemple, vous pouvez créer votre première application Sequelize avec une connexion à la base de données.

Connexion à la base de données MySQL

Dans cette section, vous allez connecter l'application Node.js à la base de données MySQL en utilisant Sequelize.

Pour se connecter à la base de données, ouvrez server.js pour l’éditer en utilisant nano ou votre éditeur de code préféré :

nano server.js

Ici, vous allez créer une connexion à la base de données dans votre application en utilisant une instance de Sequelize. Dans la méthode new Sequelize(), passez les paramètres du serveur MySQL et les identifiants de la base de données comme suit, en remplaçant DATABASE_USERNAME et DATABASE_PASSWORD par les identifiants de votre utilisateur MySQL :

const Sequelize = require("sequelize");
const sequelize = new Sequelize(
 'hello_world_db',
 'DATABASE_USERNAME',
 'DATABASE_PASSWORD',
  {
    host: 'DATABASE_HOST',
    dialect: 'mysql'
  }
);

host est l'endroit où le serveur MySQL est hébergé, vous devrez donc fournir une URL de serveur ou une adresse IP. Si vous utilisez un serveur MySQL installé localement, vous pouvez remplacer DATABASE_HOST par localhost ou 127.0.0.1 comme valeur.

De même, si vous utilisez un serveur distant, assurez-vous de remplacer les valeurs de connexion à la base de données par les détails appropriés du serveur distant.

Remarque : Si vous utilisez un autre logiciel de serveur de base de données, vous pouvez remplacer le paramètre de dialecte en conséquence.

Ensuite, appelez une méthode authenticate() basée sur les promesses pour créer une connexion à la base de données pour l'application. Pour ce faire, ajoutez le bloc de code suivant à votre fichier server.js :

...

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

La méthode authenticate() est utilisée pour se connecter à la base de données et vérifier si les identifiants fournis sont corrects. Ici, la connexion à la base de données est ouverte par défaut et la même connexion peut être utilisée pour toutes les requêtes. Chaque fois que vous devez fermer la connexion, appelez la méthode sequelize.close() après cet appel à authenticate(). Pour en savoir plus sur Sequelize, veuillez consulter leur guide de démarrage.

La plupart des méthodes fournies par Sequelize sont asynchrones. Cela signifie que vous pouvez exécuter des processus dans votre application pendant qu'un bloc de code asynchrone est en cours d'exécution. De plus, après l'exécution réussie d'un bloc de code asynchrone, il retourne une promesse, qui est la valeur renvoyée à la fin d'un processus. Par conséquent, dans les blocs de code asynchrones, vous pouvez utiliser then(), catch() et finally() pour retourner les données traitées.

À ce stade, le fichier server.js ressemblera à ce qui suit :

const Sequelize = require("sequelize");

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Enregistrez et fermez votre fichier.

Dans le répertoire du projet, exécutez l'application server.js en utilisant la commande suivante :

node server.js

Votre sortie ressemblera à ceci :

Connection has been established successfully!

Vous avez créé la connexion à la base de données avec succès.

Dans cette étape, vous avez installé Sequelize, créé une base de données exemple et utilisé Sequelize pour vous connecter à la base de données. Ensuite, vous travaillerez avec les modèles dans Sequelize.

Créer une table de base de données avec Sequelize

Maintenant que vous avez créé une base de données MySQL d'exemple, vous pouvez utiliser Sequelize pour créer une table et la remplir de données. Dans Sequelize, les tables de la base de données sont appelées modèles. Un modèle est une abstraction qui représente une table de la base de données. Les modèles définissent plusieurs éléments pour Sequelize, tels que le nom de la table, les détails des colonnes et les types de données. Dans cette étape, vous allez créer un modèle Sequelize pour les données de livres.

Pour commencer, créez un nouveau fichier appelé book.model.js dans le répertoire du projet :

nano book.model.js

Similaire à l'étape précédente, ajoutez le code Sequelize pour l'initialisation de la base de données avec une nouvelle importation de DataTypes en haut du fichier :

const { Sequelize, DataTypes } = require("sequelize");

Sequelize contient de nombreux types de données intégrés. Pour accéder à ces types de données, vous devez ajouter une importation pour DataTypes. Ce tutoriel fait référence à certains types de données fréquemment utilisés, tels que STRING, INTEGER et DATEONLY. Pour en savoir plus sur les autres types de données pris en charge, vous pouvez consulter la documentation officielle de Sequelize.

Ensuite, incluez les lignes que vous avez utilisées précédemment pour créer une connexion à votre base de données MySQL, en mettant à jour vos informations d'identification MySQL en conséquence :

...

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Ensuite, vous allez créer un modèle appelé books, qui inclut title, author, release_date et l'ID de subject. Pour ce faire, utilisez la méthode sequelize.define() comme suit :

...

const Book = sequelize.define("books", {
   title: {
     type: DataTypes.STRING,
     allowNull: false
   },
   author: {
     type: DataTypes.STRING,
     allowNull: false
   },
   release_date: {
     type: DataTypes.DATEONLY,
   },
   subject: {
     type: DataTypes.INTEGER,
   }
});

La méthode sequelize.define() définit un nouveau modèle, qui représente une table dans la base de données. Ce bloc de code crée une table appelée books et y enregistre les livres selon le title, le author, le release_date et le subject.

Dans ce code, allowNull indique que la valeur de la colonne du modèle ne peut pas être null. De même, si vous devez définir une telle valeur, vous pouvez utiliser defaultValue: "value".

Ensuite, vous ajouterez le modèle book à votre base de données. Pour ce faire, vous utiliserez la méthode sync() comme suit :

...

sequelize.sync().then(() => {
   console.log('Book table created successfully!');
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Dans la méthode sync(), vous demandez à Sequelize de faire quelques opérations sur la base de données. Avec cet appel, Sequelize exécutera automatiquement une requête SQL sur la base de données et créera une table, affichant le message Table Book créée avec succès !.

Comme mentionné, la méthode sync() est une méthode basée sur les promesses, ce qui signifie qu'elle peut également gérer les erreurs. Dans ce bloc de code, vous allez vérifier si la table est créée avec succès. Sinon, elle renverra une erreur via la méthode catch et l'affichera dans la sortie.

Remarque : Vous pouvez gérer la synchronisation des modèles en passant des paramètres force pour forcer la création d'une nouvelle table si elle n'existe pas, ou utiliser une table existante. Voici quelques exemples, qui peuvent vous être utiles lors de l'utilisation de Sequelize :

  • model.sync() : Cela crée la table si elle n’existe pas déjà.
  • model.sync({ force: true }) : Cela crée la table en la supprimant si une table du même nom existe déjà.

Le code final ressemblera à ceci :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'hello_world_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Book = sequelize.define("books", {
   title: {
     type: DataTypes.STRING,
     allowNull: false
   },
   author: {
     type: DataTypes.STRING,
     allowNull: false
   },
   release_date: {
     type: DataTypes.DATEONLY,
   },
   subject: {
     type: DataTypes.INTEGER,
   }
});

sequelize.sync().then(() => {
   console.log('Book table created successfully!');
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Enregistrez et fermez votre fichier.

Exécutez votre application en utilisant la commande suivante :

node book.model.js

Vous obtiendrez la sortie suivante dans votre ligne de commande :

Executing (default): SELECT 1+1 AS result
Executing (default): CREATE TABLE IF NOT EXISTS `books` (`id` INTEGER NOT NULL auto_increment , `title` VARCHAR(255) NOT NULL, `author` VARCHAR(255) NOT NULL, `release_date` DATE, `subject` INTEGER, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB;
Connection has been established successfully.
Executing (default): SHOW INDEX FROM `books`
Book table created successfully!

Dans la sortie, vous verrez que le journal de retour contient le message, Table 'Book' créée avec succès !. Vous pouvez vérifier cela en consultant votre base de données pour voir la nouvelle table books créée dans la base de données hello_world_db.

Pour vérifier la création de la nouvelle table, connectez-vous à votre instance MySQL :

mysql -u YOUR_USERNAME -p

Après avoir saisi votre mot de passe, passez à la base de données d'exemple :

USE hello_world_db;

Puis exécutez la commande pour afficher les tables :

SHOW TABLES;

Votre sortie sera similaire à ceci :

+---------------------------+
| Tables_in_hello_world_db |
+---------------------------+
| books                     |
+---------------------------+
1 row in set (0.00 sec)

Enfin, déconnectez-vous du serveur MySQL :

mysql> QUIT

Vous avez vérifié que la création du modèle book a réussi. En utilisant ce processus, vous pouvez créer autant de modèles que vous le souhaitez en suivant la même procédure.

Dans cette étape, vous avez créé un modèle dans une base de données et commencé à travailler avec un modèle en utilisant les méthodes intégrées. Vous avez également utilisé les types de données pris en charge par Sequelize pour définir votre modèle. Ensuite, vous travaillerez avec des requêtes de base sur le modèle.

Utilisation de Sequelize pour les requêtes de base de données

Dans cette étape, vous utiliserez les requêtes intégrées de Sequelize pour l'insertion, la sélection, la sélection avec des clauses conditionnelles et la suppression.

Insertion d’un nouvel enregistrement

Dans l'étape précédente, vous avez créé un modèle book dans la base de données. Dans cette section, vous allez insérer des données dans ce modèle.

Pour commencer, copiez le contenu de book.model.js de l'étape précédente. Créez un nouveau fichier appelé book.controller.js pour gérer la logique des requêtes. Ajoutez le code de book.model.js dans book.controller.js.

Dans book.controller.js, localisez la méthode sync(). Dans la méthode sync(), ajoutez les lignes surlignées suivantes :

...

sequelize.sync().then(() => {
   console.log('Book table created successfully!');

   Book.create({
       title: "Clean Code",
       author: "Robert Cecil Martin",
       release_date: "2021-12-14",
       subject: 3
   }).then(res => {
       console.log(res)
   }).catch((error) => {
       console.error('Failed to create a new record : ', error);
   });

}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Ici, vous insérez un nouvel enregistrement de livre dans le modèle books que vous avez déjà créé en utilisant la méthode sync(), qui permet d’ajouter de nouveaux enregistrements à des modèles déjà créés. Une fois que la méthode sync() s’exécute avec succès, elle lance la méthode then(). À l’intérieur de la méthode then(), vous appelez la méthode create() pour insérer les nouveaux enregistrements dans le modèle.

Vous utilisez la méthode create() pour transmettre les données que vous souhaitez ajouter à la base de données sous forme d'objet. La section de code surlignée insérera une nouvelle entrée dans votre table books existante. Dans cet exemple, vous ajoutez Clean Code de Robert Cecil Martin, lequel a été classé avec l'ID subject de 3. Vous pouvez utiliser le même code, mis à jour avec les informations d'autres livres, pour ajouter de nouveaux enregistrements à votre base de données.

Enregistrez et fermez le fichier.

Exécutez l'application en utilisant la commande suivante :

node book.controller.js

Votre sortie ressemblera à ce qui suit :

books {
  dataValues:
   { id: 1,
     title: 'Clean Code',
     author: 'Robert Cecil Martin',
     release_date: '2021-12-14',
     subject: 3,
     updatedAt: 2021-12-14T10:12:16.644Z,
   ...
}

Vous avez ajouté un nouvel enregistrement au modèle que vous avez créé dans la base de données. Vous pouvez continuer à ajouter plusieurs enregistrements en utilisant le même processus.

Sélectionner tous les enregistrements

Dans cette section, vous allez sélectionner et obtenir tous les enregistrements de livres de la base de données en utilisant la méthode findAll(). Pour ce faire, ouvrez d'abord book.controller.js et supprimez la méthode Book.create() précédente. Dans la méthode sync(), ajoutez la méthode Book.findAll() comme indiqué :

...

sequelize.sync().then(() => {

    Book.findAll().then(res => {
        console.log(res)
    }).catch((error) => {
        console.error('Failed to retrieve data : ', error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

...

Enregistrez et fermez le fichier.

Ensuite, exécutez de nouveau l’application en utilisant la commande suivante :

node book.controller.js

Votre sortie ressemblera à ce qui suit :

[
  books {
    dataValues: {
      id: 1,
      title: 'Clean Code',
      author: 'Robert Cecil Martin',
      release_date: '2020-01-01',
      subject: 3,
      createdAt: 2021-02-22T09:13:55.000Z,
      updatedAt: 2021-02-22T09:13:55.000Z
    },
    _previousDataValues: {
      id: 1,
      title: 'Clean Code',
      author: 'Robert Cecil Martin',
      release_date: '2020-01-01',
      subject: 3,
      createdAt: 2021-02-22T09:13:55.000Z,
      updatedAt: 2021-02-22T09:13:55.000Z
    },
...
]

La sortie contient toutes les données des livres sous forme de tableau d'objets. Vous avez utilisé avec succès la méthode Sequelize findAll() pour renvoyer toutes les données des livres de la base de données.

Sélection avec la clause where

Dans cette section, vous allez sélectionner des valeurs avec des conditions en utilisant la clause where. La clause where est utilisée pour spécifier une condition lors de la récupération des données. Pour ce tutoriel, vous récupérerez un livre en utilisant un ID d'enregistrement spécifique de la base de données avec la méthode findOne().

Pour ce faire, ouvrez book.controller.js pour l’éditer, supprimez la méthode findAll() et ajoutez les lignes suivantes :

...

sequelize.sync().then(() => {

    Book.findOne({
        where: {
            id: 1
        }
    }).then(res => {
        console.log(res)
    }).catch((error) => {
        console.error('Failed to retrieve data : ', error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ici, vous sélectionnez un enregistrement de livre spécifique dans la base de données en utilisant la méthode findOne() avec l'option where. Dans cet exemple, vous récupérez les données du livre dont le id est égal à 1.

Enregistrez et fermez le fichier.

Ensuite, lancez l'application :

node book.controller.js

Votre sortie ressemblera à ce qui suit :

books {
  dataValues: {
    id: 1,
    title: 'Clean Code',
    author: 'Robert Cecil Martin',
    release_date: '2020-01-01',
    subject: 'Science',
    createdAt: 2021-02-22T09:13:55.000Z,
    updatedAt: 2021-02-22T09:13:55.000Z
  },
  ...
}

Vous avez utilisé avec succès les clauses where pour obtenir des données à partir des modèles Sequelize. Vous pouvez utiliser la clause where dans l'application de base de données pour récupérer des données conditionnelles.

Suppression d’un enregistrement

Pour supprimer un enregistrement spécifique du modèle de base de données, vous utilisez la méthode destroy() avec l’option where. Pour ce faire, ouvrez book.controller.js, supprimez la méthode findOne() et ajoutez les lignes suivantes mises en évidence :

...
sequelize.sync().then(() => {

  Book.destroy({
      where: {
        id: 2
      }
  }).then(() => {
      console.log("Successfully deleted record.")
  }).catch((error) => {
      console.error('Failed to delete record : ', error);
  });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ici, vous supprimez un enregistrement de livre de la base de données en utilisant la méthode destroy() avec l'option where et en passant l'id du livre à supprimer. Vous allez supprimer l'enregistrement du livre dont l'id est égal à 2.

Enregistrez et fermez le fichier.

Ensuite, lancez l'application :

node book.controller.js

Votre sortie ressemblera à ce qui suit :

Successfully deleted record.

Le dossier a été supprimé.

À cette étape, vous avez expérimenté avec votre modèle de base de données et vos requêtes de modèle. Vous avez initialisé la base de données, créé des modèles, inséré des enregistrements, récupéré des enregistrements, récupéré des enregistrements avec des conditions en utilisant la clause where, et supprimé des enregistrements sélectionnés. Avec cette connaissance de Sequelize, vous allez maintenant créer des associations dans Sequelize. Après cela, vous serez en mesure de définir et de travailler avec une variété de relations en utilisant les modèles Sequelize.

Créer des associations avec Sequelize

Dans cette étape, vous utiliserez les types d’association standard pris en charge par Sequelize : un-à-un, un-à-plusieurs et plusieurs-à-plusieurs. Vous utiliserez des données d’exemple concernant les étudiants, les cours et les niveaux de notes.

Sequelize utilise des types d'association basés sur les relations de base de données suivantes :

  • relation un à un : Une relation un à un signifie qu'un enregistrement dans une table est associé à exactement un enregistrement dans une autre table. En termes de Sequelize, vous pouvez utiliser les associations belongsTo() et hasOne() pour créer ce type de relation.

  • relation un-à-plusieurs : Une relation un-à-plusieurs signifie qu'un enregistrement dans une table est associé à plusieurs enregistrements dans une autre table. Avec Sequelize, vous pouvez utiliser les méthodes d'association hasMany() pour créer ce type de relation.

  • Relation plusieurs-à-plusieurs : Une relation plusieurs-à-plusieurs signifie que plusieurs enregistrements dans une table sont associés à plusieurs enregistrements dans une autre table. Avec Sequelize, vous pouvez utiliser les associations belongsToMany() pour créer ce type de relation.

Avant de créer ces associations, vous allez d'abord créer une nouvelle base de données appelée student_db et ajouter de nouveaux modèles ainsi que des données d'exemple pour les étudiants, les cours et le niveau scolaire.

Pour créer la base de données, suivez le même processus que lors de l'installation et de la configuration de Sequelize pour vous connecter à MySQL et créer une base de données appelée student_db. Une fois la nouvelle base de données créée, déconnectez-vous de MySQL. Ensuite, vous commencerez à créer les associations de la base de données.

Créer une relation un-à-un avec belongsTo()

Dans cette section, vous allez créer une relation un-à-un en utilisant les modèles Sequelize. Imaginez que vous souhaitez obtenir les détails d’un étudiant ainsi que son niveau de grade. Puisqu’un étudiant ne peut avoir qu’un seul niveau de grade, ce type d’association est une relation un-à-un et vous pouvez utiliser la méthode belongsTo().

Remarque : Il y a une différence entre belongsTo() et hasOne(). belongsTo() ajoutera la foreignKey dans la table source, tandis que hasOne() l'ajoutera dans la table cible. Dans tous les cas, si les deux relations sont utilisées en même temps, cela fonctionnera comme des relations un-à-un bidirectionnelles dans Sequelize.

La méthode belongsTo() vous permet de créer une relation un-à-un entre deux modèles Sequelize. Dans cet exemple, vous utilisez les modèles Student et Grade.

Créez un nouveau fichier nommé one_to_one.js. Comme vous l'avez fait dans la section précédente, Connexion à la base de données MySQL, incluez en haut du fichier les lignes permettant de créer une connexion à la base de données et d'authentifier votre utilisateur MySQL. Assurez-vous de mettre à jour les informations d'identification MySQL si nécessaire :

const { Sequelize, DataTypes } = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

Dans cette section, vous allez créer trois modèles dans la nouvelle base de données student_db : Student, Grade et Course. Vous commencerez par créer les modèles Student et Grade. Plus tard dans cette étape, vous créerez le modèle Course.

Pour le modèle Student, ajoutez le bloc de code suivant à one_to_one.js :

...
const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});

Ce modèle d'étudiant contient deux colonnes : student_id et name.

Ensuite, ajoutez un bloc de code pour le modèle Grade :

...
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

Le modèle Grade contient la colonne grade.

Pour démontrer les associations, vous devrez ajouter des données d'exemple à la base de données. Pour cela, vous utiliserez la méthode bulk(). Plutôt que d'insérer les données ligne par ligne, la méthode bulkCreate() vous permet d'insérer plusieurs lignes dans vos modèles de base de données en une seule fois.

Alors maintenant, importez les données Grade et Student dans leurs modèles respectifs dans la base de données comme montré :

...
const grade_data = [{grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           …
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Ici, vous fournissez des données d'exemple et insérez les données dans les modèles Student et Grade. Avec votre base de données, vos modèles et vos données d'exemple en place, vous êtes prêt à créer des associations.

Dans one-to-one.js, ajoutez la ligne suivante sous le bloc student_data :

...
Student.belongsTo(Grade);

Ensuite, vous devrez vérifier si l'association fonctionne correctement. Pour ce faire, vous pouvez récupérer les données de tous les étudiants avec les niveaux de grade associés en passant le paramètre include dans la méthode findAll().

Puisque vous devez obtenir le niveau scolaire de l’étudiant, vous passerez Grade comme modèle. Dans la méthode sequelize.sync(), ajoutez les lignes surlignées comme indiqué :

...
sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Student.findAll({
               include: [{
                   model: Grade
               }]
           }).then(result => {
               console.log(result)
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Le code complet ressemble à ce qui suit :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );
sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});

const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [{grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

// One-To-One association
Student.belongsTo(Grade);

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Student.findAll({
               include: [{
                   model: Grade
               }]
           }).then(result => {
               console.log(result)
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create the table : ', error);
});

Enregistrez et fermez votre fichier.

Exécutez le fichier en utilisant la commande suivante :

node one_to_one.js

Le résultat sera long et vous verrez les données de tous les élèves avec leurs niveaux de cours. Voici un extrait du résultat montrant les données des élèves :

students {
    dataValues:
   { student_id: '3e786a8f-7f27-4c59-8e9c-a8c606892288',
       name: 'Sam Lewis',
       createdAt: 2021-12-16T08:49:38.000Z,
       updatedAt: 2021-12-16T08:49:38.000Z,
       gradeId: 1,
       grade: [grades] },
    _previousDataValues:
...

Selon les outils en ligne de commande que vous utilisez, la sortie peut s'afficher sous forme de vue développée ou non. Si c'est une vue développée, elle affiche l'objet grade développé comme sortie.

Dans cette section, vous avez créé une relation un-à-un en utilisant l'appel de méthode Student.belongsTo(Grade); et vous avez obtenu les détails selon l'association que vous avez créée.

Créer une relation un-à-plusieurs avec hasMany()

Dans cette section, vous allez créer une relation un-à-plusieurs en utilisant les modèles Sequelize. Imaginez que vous souhaitez obtenir tous les étudiants associés à un niveau scolaire sélectionné. Puisqu’un niveau scolaire spécifique peut avoir plusieurs étudiants, il s’agit d’une relation un-à-plusieurs.

Pour commencer, copiez le contenu de one_to_one.js dans un nouveau fichier appelé one_to_many.js. Dans one_to_many.js, supprimez les lignes après le bloc student_data. Votre fichier one_to_many.js ressemblera à ceci :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [ {grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

Après le bloc student_data, utilisez la méthode hasMany() pour créer une nouvelle relation :

...
Grade.hasMany(Student)

La méthode hasMany() vous permet de créer une relation un-à-plusieurs entre deux modèles Sequelize. Ici, vous utilisez les modèles Grade et Student.

Ensuite, ajoutez la méthode sequelize.sync() avec la méthode findAll() sous la ligne hasMany() :

...
sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Grade.findAll({
               where: {
                   grade: 9
               },
               include: [{
                   model: Student
               }]
           }).then(result => {
               console.dir(result, { depth: 5 });
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Ici, vous essayez d'accéder à tous les étudiants d'un niveau de classe particulier — dans ce cas, tous les étudiants de la 9e année. Vous avez également ajouté le modèle Student dans l'option include.

Voici le code complet :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
  );

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
   student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
       primaryKey: true,
   },
   name: {
       type: DataTypes.STRING,
       allowNull: false
   }
});
const Grade = sequelize.define("grades", {
   grade: {
       type: DataTypes.INTEGER,
       allowNull: false
   }
});

const grade_data = [ {grade : 9}, {grade : 10}, {grade : 11}]

const student_data = [
   {name : "John Baker", gradeId: 2},
   {name : "Max Butler", gradeId: 1},
   {name : "Ryan Fisher", gradeId: 3},
   {name : "Robert Gray", gradeId: 2},
   {name : "Sam Lewis", gradeId: 1}
]

// One-To-Many relationship
Grade.hasMany(Student);

sequelize.sync({ force: true }).then(() => {
   Grade.bulkCreate(grade_data, { validate: true }).then(() => {
       Student.bulkCreate(student_data, { validate: true }).then(() => {
           Grade.findAll({
               where: {
                   grade: 9
               },
               include: [{
                   model: Student
               }]
           }).then(result => {
               console.dir(result, { depth: 5 });
           }).catch((error) => {
               console.error('Failed to retrieve data : ', error);
           });
       }).catch((err) => { console.log(err); });
   }).catch((err) => { console.log(err); });
}).catch((error) => {
   console.error('Unable to create table : ', error);
});

Enregistrez et fermez votre fichier.

Exécutez le fichier avec la commande suivante :

node one_to_many.js

Le résultat ressemblera à ce qui suit. Il sera assez long, mais tous les élèves de la 9e année seront affichés comme suit :

[ grades {
    dataValues:
     { id: 1,
       grade: 9,
       createdAt: 2021-12-20T05:12:31.000Z,
       updatedAt: 2021-12-20T05:12:31.000Z,
       students:
        [ students {
            dataValues:
             { student_id: '8a648756-4e22-4bc0-8227-f590335f9965',
               name: 'Sam Lewis',
               createdAt: 2021-12-20T05:12:31.000Z,
               updatedAt: 2021-12-20T05:12:31.000Z,
               gradeId: 1 },
...
          students {
            dataValues:
             { student_id: 'f0304585-91e5-4efc-bdca-501b3dc77ee5',
               name: 'Max Butler',
               createdAt: 2021-12-20T05:12:31.000Z,
               updatedAt: 2021-12-20T05:12:31.000Z,
               gradeId: 1 },
...

Dans cette section, vous avez créé une relation un-à-plusieurs en utilisant l'appel de méthode Grade.hasMany(Student);. Dans le résultat, vous avez récupéré les détails selon l'association que vous avez créée.

Créer des relations plusieurs-à-plusieurs avec belongsToMany()

Dans cette section, vous allez créer des relations plusieurs-à-plusieurs en utilisant les modèles Sequelize. Par exemple, imaginez une situation où des étudiants sont inscrits à des cours. Un étudiant peut s’inscrire à plusieurs cours et un cours peut avoir plusieurs étudiants. Il s’agit d’une relation plusieurs-à-plusieurs. Pour l’implémenter avec Sequelize, vous utiliserez les modèles Student, Course et StudentCourse avec la méthode belongsToMany().

Pour commencer, créez un fichier appelé many_to_many.js et ajoutez les blocs de code d'initialisation et d'authentification de la base de données comme suit. (Vous pouvez réutiliser les blocs de code de l'exemple précédent one_to_many.js.) Assurez-vous de mettre à jour les valeurs de connexion à la base de données surlignées si nécessaire.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
);

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

Ensuite, vous allez créer les modèles de base de données pour les relations plusieurs-à-plusieurs : Student et Course. Puis, vous ajouterez quelques données d'exemple à ces modèles.

...

const Student = sequelize.define("students", {
    student_id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
    },
    name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const Course = sequelize.define("courses", {
    course_name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const StudentCourse = sequelize.define('StudentCourse', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false
    }
  });

const course_data = [
    {course_name : "Science"},
    {course_name : "Maths"},
    {course_name : "History"}
]

const student_data = [
    {name : "John Baker", courseId: 2},
    {name : "Max Butler", courseId: 1},
    {name : "Ryan Fisher", courseId: 3},
    {name : "Robert Gray", courseId: 2},
    {name : "Sam Lewis", courseId: 1}
]

const student_course_data = [
    {studentId : 1, courseId: 1},
    {studentId : 2, courseId: 1},
    {studentId : 2, courseId: 3},
    {studentId : 3, courseId: 2},
    {studentId : 1, courseId: 2},
]

Ici, vous créez les modèles Student et Course et fournissez quelques données d'exemple. Vous définissez également un courseID, que vous utiliserez pour récupérer les étudiants selon ce type de relation.

Enfin, vous avez défini un nouveau modèle appelé StudentCourse, qui gère les données de relation entre Student et Course. Dans cet exemple, studentId 1 est inscrit à courseId 1 et courseId 2.

Vous avez terminé l'initialisation de la base de données et ajouté des données d'exemple à la base de données. Ensuite, créez des relations plusieurs-à-plusieurs en utilisant la méthode belongsToMany() comme montré :

...
Course.belongsToMany(Student, { through: 'StudentCourse'})
Student.belongsToMany(Course, { through: 'StudentCourse'})

Dans la méthode belongsToMany(), vous passez la configuration through avec le nom du modèle comme option de configuration. Dans ce cas, il s'agit de StudentCourse. C'est la table qui gère les relations plusieurs-à-plusieurs.

Enfin, vous pouvez vérifier si l'association fonctionne correctement en récupérant toutes les données des cours avec les étudiants associés. Vous ferez cela en passant le paramètre include dans la méthode findAll(). Ajoutez les lignes suivantes à many_to_many.js :

...
sequelize.sync({ force: true }).then(() => {
    Course.bulkCreate(course_data, { validate: true }).then(() => {
        Student.bulkCreate(student_data, { validate: true }).then(() => {
            StudentCourse.bulkCreate(student_course_data, { validate: true }).then(() => {
                Course.findAll({
                    include: {
                        model: Student,
                    },
                }).then(result => {
                    console.log(result);
                }).catch((error) => {
                    console.error('Failed to retrieve data : ', error);
                });
            }).catch((error) => {
                console.log(error);
            });
        }).catch((error) => {
            console.log(error);
        });
    }).catch((error) => {
        console.log(error);
    });
}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Le code complet ressemble à ce qui suit :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
);

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
    student_id: {
       type: DataTypes.UUID,
       defaultValue: DataTypes.UUIDV4,
    },
    name: {
       type: DataTypes.STRING,
       allowNull: false
    }
});

const Course = sequelize.define("courses", {
    course_name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const StudentCourse = sequelize.define('StudentCourse', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false
    }
  });

const course_data = [
    {course_name : "Science"},
    {course_name : "Maths"},
    {course_name : "History"}
]

const student_data = [
    {name : "John Baker", courseId: 2},
    {name : "Max Butler", courseId: 1},
    {name : "Ryan Fisher", courseId: 3},
    {name : "Robert Gray", courseId: 2},
    {name : "Sam Lewis", courseId: 1}
]

const student_course_data = [
    {studentId : 1, courseId: 1},
    {studentId : 2, courseId: 1},
    {studentId : 2, courseId: 3},
    {studentId : 3, courseId: 2},
    {studentId : 1, courseId: 2},
]

Course.belongsToMany(Student, { through: 'StudentCourse'})
Student.belongsToMany(Course, { through: 'StudentCourse'})

sequelize.sync({ force: true }).then(() => {
    Course.bulkCreate(course_data, { validate: true }).then(() => {
        Student.bulkCreate(student_data, { validate: true }).then(() => {
            StudentCourse.bulkCreate(student_course_data, { validate: true }).then(() => {
                Course.findAll({
                    include: {
                        model: Student,
                    },
                }).then(result => {
                    console.log(result);
                }).catch((error) => {
                    console.error('Failed to retrieve data : ', error);
                });
            }).catch((error) => {
                console.log(error);
            });
        }).catch((error) => {
            console.log(error);
        });
    }).catch((error) => {
        console.log(error);
    });
}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Enregistrez et fermez le fichier.

Exécutez le fichier en utilisant la commande suivante :

node many_to_many.js

La sortie sera longue, mais ressemblera à quelque chose de similaire à ce qui suit :

[ courses {
    dataValues:
     { id: 1,
       course_name: 'Science',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _previousDataValues:
     { id: 1,
       course_name: 'Science',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _changed: Set {},
    _options:
     { isNewRecord: false,
       _schema: null,
       _schemaDelimiter: '',
       include: [Array],
       includeNames: [Array],
       includeMap: [Object],
       includeValidated: true,
       attributes: [Array],
       raw: true },
    isNewRecord: false,
    students: [ [students], [students] ] },
  courses {
    dataValues:
     { id: 2,
       course_name: 'Maths',
       createdAt: 2022-05-11T04:27:37.000Z,
       updatedAt: 2022-05-11T04:27:37.000Z,
       students: [Array] },
    _previousDataValues:
...

Comme vous pouvez le voir dans cette sortie, les cours avec les étudiants associés ont été récupérés. Dans le bloc courses, vous verrez des valeurs id séparées qui indiquent chaque cours. Par exemple, id: 1 est relié au course_name: Science pour la classe de Science, tandis que id: 2 correspond à la classe de Mathématiques, et ainsi de suite.

Dans la base de données, vous pouvez voir les trois tables générées avec les données d'exemple que vous avez insérées.

Dans cette étape, vous avez utilisé Sequelize pour créer des associations un-à-un, un-à-plusieurs et plusieurs-à-plusieurs. Ensuite, vous travaillerez avec des requêtes brutes.

Travailler avec des requêtes brutes

À cette étape, vous travaillerez avec des requêtes brutes dans Sequelize. Dans les étapes précédentes, vous avez utilisé les méthodes intégrées de Sequelize, telles que insert() et findAll(), pour gérer l’insertion et la sélection de données dans la base de données. Vous avez peut-être remarqué que ces méthodes suivent un modèle spécifique pour écrire une requête. Cependant, avec l’utilisation de requêtes brutes, vous n’avez pas besoin de vous soucier des méthodes et des modèles intégrés de Sequelize. En utilisant vos connaissances des requêtes SQL, vous pouvez effectuer une gamme de requêtes dans Sequelize, des plus simples aux plus avancées.

Voici un exemple de requêtes brutes qui effectuent l'action de sélectionner toutes les valeurs d'une table particulière, de supprimer les valeurs sélectionnées selon la condition, et de mettre à jour la table avec les valeurs données.

SELECT * FROM table_name;
DELETE FROM table_name WHERE condition;
UPDATE table_name SET y = 42 WHERE x = 12;

Dans Sequelize, les requêtes brutes peuvent être utilisées principalement selon deux méthodes : le remplacement par tableau et le remplacement par objet. Lorsque vous passez des valeurs à la requête SQL, vous pouvez utiliser soit un tableau, soit un objet pour effectuer ce remplacement.

Avant d'écrire une requête brute, vous devrez d'abord fournir des données d'étudiants dans une base de données d'exemple. En suivant la section précédente, Création d'une base de données d'exemple, connectez-vous à MySQL, créez une base de données appelée sample_student_db, puis déconnectez-vous de MySQL.

Ensuite, vous ajouterez des données brutes pour commencer à travailler avec des requêtes brutes. Créez un nouveau fichier appelé add_student_records.js et ajoutez les blocs de code suivants, qui contiennent les méthodes Sequelize précédemment discutées : authenticate(), sync() et bulkCreate().

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

const Student = sequelize.define("students", {
    student_id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        primaryKey: true,
    },
    name: {
        type: DataTypes.STRING,
        allowNull: false
    }
});

const student_data = [
    {name : "John Baker"},
    {name : "Max Butler"},
    {name : "Ryan Fisher"},
    {name : "Robert Gray"},
    {name : "Sam Lewis"}
]

sequelize.sync({ force: true }).then(() => {

    Student.bulkCreate(student_data, { validate: true }).then((result) => {
        console.log(result);
    }).catch((error) => {
        console.log(error);
    });

}).catch((error) => {
    console.error('Unable to create table : ', error);
});

Ici, vous initiez la connexion à la base de données, créez le modèle et insérez quelques enregistrements d'étudiants dans la nouvelle base de données.

Enregistrez et fermez le fichier.

Ensuite, exécutez ce script en utilisant la commande suivante :

node add_student_records.js

Le résultat sera quelque chose de similaire à ce qui suit. Ce sera assez long, mais tous les dossiers des étudiants que vous avez insérés seront retournés comme suit. Notez que puisque le student_id est une valeur UUID (Identifiant Unique Universel) générée automatiquement, elle sera différente selon l'utilisateur.

Executing (default): SELECT 1+1 AS result
Executing (default): DROP TABLE IF EXISTS `students`;
Connection has been established successfully.
Executing (default): DROP TABLE IF EXISTS `students`;
Executing (default): CREATE TABLE IF NOT EXISTS `students` (`student_id` CHAR(36) BINARY , `name` VARCHAR(255) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`student_id`)) ENGINE=InnoDB;
Executing (default): SHOW INDEX FROM `students`
Executing (default): INSERT INTO `students` (`student_id`,`name`,`createdAt`,`updatedAt`) VALUES ('45d1f26c-ba76-431f-ac5f-f41282351710','John Baker','2022-06-03 07:27:49','2022-06-03 07:27:49'),('1cb4e34d-bfcf-4a97-9624-e400b9a1a5f2','Max Butler','2022-06-03 07:27:49','2022-06-03 07:27:49'),('954c576b-ba1c-4dbc-a5c6-8eaf22bbbb04','Ryan Fisher','2022-06-03 07:27:49','2022-06-03 07:27:49'),('e0f15cd3-0025-4032-bfe8-774e38e14c5f','Robert Gray','2022-06-03 07:27:49','2022-06-03 07:27:49'),('826a0ec9-edd0-443f-bb12-068235806659','Sam Lewis','2022-06-03 07:27:49','2022-06-03 07:27:49');
[
  students {
    dataValues: {
      student_id: '45d1f26c-ba76-431f-ac5f-f41282351710',
      name: 'John Baker',
      createdAt: 2022-06-03T07:27:49.453Z,
      updatedAt: 2022-06-03T07:27:49.453Z
    },
    _previousDataValues: {
      name: 'John Baker',
      student_id: '45d1f26c-ba76-431f-ac5f-f41282351710',
      createdAt: 2022-06-03T07:27:49.453Z,
      updatedAt: 2022-06-03T07:27:49.453Z
    },

…

Dans la section suivante, vous allez appliquer des requêtes brutes en utilisant l'une des sorties student_id dans le bloc de code ci-dessus. Copiez-la afin de l'avoir pour les sections suivantes, où vous utiliserez la méthode query() pour les remplacements de tableaux et d'objets.

Remplacement de tableau

Dans cette section, vous utiliserez la méthode query() pour remplacer un tableau. Avec cette méthode, Sequelize peut exécuter des requêtes SQL brutes ou déjà préparées.

Pour commencer, copiez le contenu du fichier server.js de l'étape 1, puisqu'il inclut la méthode d'initialisation Sequelize() et l'initialisation de la base de données. Collez le contenu dans un nouveau fichier appelé array_raw_query.js. Mettez à jour le nom de la base de données pour sample_student_db :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

À la fin du fichier, ajoutez le bloc de code suivant pour un remplacement de tableau, en vous assurant de remplacer REPLACE_STUDENT_ID par la valeur student_id que vous avez copiée dans la section précédente.

...
sequelize.query(
    'SELECT * FROM students WHERE student_id = ?',
    {
      replacements: ['REPLACE_STUDENT_ID'],
      type: sequelize.QueryTypes.SELECT
    }
).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Pour le remplacement de tableau, vous passez à la méthode query() la requête SQL et l'objet de configuration. Il contient la valeur et le type de replacements. Pour les remplacements, vous passez les données sous forme de tableau et récupérez ces valeurs en utilisant le symbole point d'interrogation (?).

Ensuite, comme vous devez obtenir des données sur un étudiant spécifique, le student_id est passé comme deuxième paramètre. Après cela, vous passez la paire clé-valeur type: sequelize.QueryTypes.SELECT, que vous pouvez utiliser pour sélectionner des données dans la base de données.

Il existe également d'autres types, tels que QueryTypes.UPDATE et QueryTypes.DELETE. Selon vos besoins, vous pouvez choisir le type qui convient à votre objectif.

Ce qui suit montre le bloc de code complet. Ici, vous vous connectez à la base de données et récupérez les données de l'étudiant sélectionné à l'aide d'une requête brute.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

sequelize.query(
    'SELECT * FROM students WHERE student_id = ?',
    {
      replacements: ['REPLACE_STUDENT_ID'],
      type: sequelize.QueryTypes.SELECT
    }
).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Enregistrez et fermez votre fichier.

Ensuite, vous pouvez exécuter ce script en utilisant la commande suivante :

node array_raw_query.js

Vous verrez une sortie semblable à ce qui suit :

Connection has been established successfully.
[ { student_id: 'STUDENT_ID_YOU_RETRIEVED',
    name: 'Robert Gray',
    createdAt: 2022-05-06T13:14:50.000Z,
    updatedAt: 2022-05-06T13:14:50.000Z } ]

En raison du student_id sélectionné, vos valeurs de sortie peuvent différer.

Remplacement d'objet

En surface, le remplacement d'objet est similaire au remplacement de tableau, mais le mode de transmission des données à la requête brute est différent. Dans l'option de remplacement, vous passez les données sous forme d'objet, et dans l'option de requête, vous utilisez des valeurs comme :key.

Pour commencer, créez un nouveau fichier appelé object_raw_query.js et collez les blocs de code complets du fichier server.js, en mettant à jour la base de données vers sample_student_db.

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

Ensuite, ajoutez le bloc de code suivant à la fin du nouveau fichier object_raw_query.js :

...
sequelize.query(
  'SELECT * FROM students WHERE student_id = :id',
  {
    replacements: { id: 'REPLACE_STUDENT_ID' },
    type: sequelize.QueryTypes.SELECT
  }
 ).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Ici, vous obtenez les données d'un étudiant sélectionné en utilisant la méthode de remplacement d'objet. Vous créez un objet replacement, en définissant le id comme les informations de l'étudiant que vous souhaitez récupérer : { id : 'REPLACE_STUDENT_ID' }.

Dans la query(), vous indiquez : 'SELECT * FROM students WHERE student_id=:id'. En utilisant la méthode query(), vous passez la valeur de remplacement sous forme d’objet, c’est pourquoi cette méthode est connue sous le nom de remplacement par objet.

Voici le code complet :

const {Sequelize, DataTypes} = require("sequelize");

const sequelize = new Sequelize(
   'sample_student_db',
   'DATABASE_USERNAME',
   'DATABASE_PASSWORD',
    {
      host: 'DATABASE_HOST',
      dialect: 'mysql'
    }
   );

sequelize.authenticate().then(() => {
    console.log('Connection has been established successfully.');
}).catch((error) => {
    console.error('Unable to connect to the database: ', error);
});

sequelize.query(
  'SELECT * FROM students WHERE student_id = :id',
  {
    replacements: { id: 'REPLACE_STUDENT_ID' },
    type: sequelize.QueryTypes.SELECT
  }
 ).then(result => {
    console.log(result);
}).catch((error) => {
    console.error('Failed to insert data : ', error);
});

Enregistrez et fermez le fichier.

Ensuite, exécutez ce script en utilisant la commande suivante :

node object_raw_query.js

La sortie ressemblera à ce qui suit :

Connection has been established successfully.
[ { student_id: 'STUDENT_ID_YOU_RETRIEVED',
    name: 'Robert Gray',
    createdAt: 2022-05-06T13:14:50.000Z,
    updatedAt: 2022-05-06T13:14:50.000Z } ]

En raison du student_id sélectionné, vos valeurs de sortie peuvent différer.

Dans cette étape, vous avez travaillé avec des requêtes brutes Sequelize en utilisant deux méthodologies différentes : le remplacement par tableau et le remplacement par objet.

Configuration des variables d'environnement pour des connexions sécurisées à la base de données

Jusqu'à présent, vous avez stocké les informations d'identification de la base de données directement dans votre code. Bien que cette approche fonctionne pour des expérimentations locales, elle n'est pas sécurisée. Intégrer les informations d'identification directement dans le code expose des informations sensibles telles que les mots de passe et les noms d'hôte de la base de données, et augmente le risque de violations lorsque le code est partagé ou déployé. Dans cette section, vous apprendrez comment stocker ces informations de manière sécurisée en utilisant des variables d'environnement.

Pourquoi utiliser des variables d'environnement ?

Les variables d’environnement agissent comme des paramètres de configuration externes que votre application peut lire à l’exécution. Plutôt que d’intégrer directement des informations d’identification dans votre code, elles sont stockées dans votre système ou dans un fichier de configuration séparé qui n’est jamais engagé dans le contrôle de version. Cette approche offre plusieurs avantages :

  • Sécurité : Vos informations d'identification restent cachées des dépôts publics et des systèmes de contrôle de version.
  • Flexibilité : Chaque environnement (développement, préproduction, production) peut utiliser une configuration de base de données différente sans modifier le code source.
  • Maintenabilité : Une configuration centralisée permet des mises à jour faciles ; si le mot de passe de la base de données change, vous n'avez qu'à modifier un seul fichier ou une seule variable.
  • Conformité : Cette pratique est conforme à la méthodologie Twelve-Factor App, qui favorise la gestion des configurations spécifiques à l'environnement pour les applications évolutives.

Configuration des variables d'environnement

Vous allez utiliser le paquet dotenv pour gérer vos variables d'environnement dans Node.js. Cette bibliothèque lit les variables à partir d'un fichier .env et les injecte dans l'objet process.env de Node.js, les rendant accessibles dans toute votre application.

Installez le paquet dotenv en exécutant la commande suivante :

npm install dotenv

Après l'installation, créez un nouveau fichier nommé .env à la racine de votre projet. Ce fichier contiendra toutes les données sensibles nécessaires à la connexion à la base de données.

Voici un exemple de configuration de fichier .env pour votre configuration Sequelize :

DB_NAME=hello_world_db
DB_USER=your_username
DB_PASSWORD=your_password
DB_HOST=localhost
DB_DIALECT=mysql

Chaque variable correspond à un paramètre spécifique que Sequelize utilise pour établir la connexion.

Sécuriser le fichier .env

Il est crucial que votre fichier .env n'apparaisse jamais dans le contrôle de version. Pour éviter cela, ajoutez-le à votre fichier .gitignore afin que Git l'ignore lors des commits :

.env

Dans les environnements de production, les variables d’environnement sont généralement configurées directement dans les paramètres de votre plateforme d’hébergement ou dans le gestionnaire de secrets, plutôt que dans un fichier .env local.

Mise à jour de votre configuration Sequelize

Maintenant que vous avez défini les variables d'environnement, mettez à jour votre initialisation Sequelize pour les utiliser. Commencez par importer et configurer dotenv en haut de votre fichier principal du serveur (par exemple, server.js) :

require('dotenv').config();
const Sequelize = require('sequelize');

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST,
    dialect: process.env.DB_DIALECT
  }
);

sequelize.authenticate().then(() => {
   console.log('Connection has been established successfully.');
}).catch((error) => {
   console.error('Unable to connect to the database:', error);
});
  1. La ligne require('dotenv').config() charge toutes les variables définies dans le fichier .env dans process.env.
  2. Sequelize lit les variables d'environnement lors de l'initialisation d'une nouvelle instance.
  3. Cela garantit qu’aucune information sensible comme les noms d’utilisateur ou les mots de passe n’apparaît dans votre code.

Cette approche améliore la sécurité et permet à différents environnements d'utiliser des configurations uniques simplement en modifiant les valeurs des variables d'environnement.

Vérification de la configuration

Une fois que vous avez mis à jour votre configuration, vous pouvez vérifier la connexion en exécutant votre application Node.js :

node server.js

Si votre fichier .env est correctement configuré et que le serveur MySQL fonctionne, vous verrez la sortie suivante :

Connection has been established successfully.

Si une erreur apparaît, vérifiez que :

  • Les identifiants dans votre fichier .env correspondent à ceux de votre instance MySQL.
  • Le serveur MySQL est en cours d'exécution et accessible depuis votre hôte.
  • Le fichier .env se trouve à la racine de votre projet et est correctement formaté.

Meilleures pratiques pour une configuration sécurisée

L'utilisation des variables d'environnement est une première étape solide vers une gestion sécurisée de la configuration. Pour assurer la sécurité et la fiabilité continues, considérez les pratiques exemplaires supplémentaires suivantes :

  • Utilisez des configurations séparées pour chaque environnement : Conservez différents fichiers .env pour développement, préproduction et production. Vous pouvez les nommer .env.development, .env.staging et .env.production.
  • Utilisez des gestionnaires de secrets : Pour les environnements de production, utilisez des gestionnaires de secrets plutôt que de stocker des secrets dans des fichiers.
  • Valider les variables d'environnement : Utilisez un utilitaire de configuration ou un script de démarrage qui vérifie si les variables requises sont définies avant que l'application ne s'exécute. Cela aide à prévenir les erreurs à l'exécution.
  • Faites tourner régulièrement les identifiants : Mettez à jour périodiquement les identifiants de la base de données pour réduire l’impact des fuites potentielles.
  • Restreindre l'accès à la base de données : Configurez les rôles des utilisateurs de la base de données afin qu'ils disposent des privilèges minimaux nécessaires à l'application.

En mettant en place des variables d'environnement et en suivant ces pratiques, vous vous assurez que votre application Sequelize maintient à la fois flexibilité et sécurité dans les environnements de développement et de production.

Bonnes pratiques en matière de migration et de synchronisation

Lors de l'évolution d'un schéma de base de données, vous avez deux approches principales avec Sequelize : écrire et exécuter des migrations explicites, ou utiliser sequelize.sync() pour laisser Sequelize générer automatiquement les modifications du schéma. Les deux approches sont valides, mais elles répondent à des objectifs différents et présentent des compromis. Voyons quand utiliser chacune, comment écrire des migrations sécuritaires et comment les exécuter de manière fiable en développement et en production.

Migrations vs sequelize.sync() — quand utiliser lequel

  • Migrations (recommandées pour la production) : Les migrations sont des modifications explicites et versionnées de votre schéma. Elles sont conservées dans le contrôle de version, examinées dans le cadre du code review et exécutées dans le cadre de votre processus de déploiement. Utilisez les migrations en production pour toute modification du schéma qui affecte l'intégrité ou la disponibilité des données.

  • sequelize.sync() (à utiliser uniquement pour le développement et les prototypes) : sync() est pratique pour le développement local, les tests et les prototypes rapides, car il peut créer ou ajuster les tables automatiquement. Cependant, n'utilisez pas sync({ force: true }) ou sync({ alter: true }) en production : ces options peuvent supprimer des données ou effectuer des modifications non sécuritaires sans voie de retour contrôlée.

Outils : sequelize-cli et exécutants de migration

L'interface en ligne de commande officielle, sequelize-cli, est la manière la plus simple de gérer les migrations avec Sequelize. Pour les équipes qui ont besoin de plus de contrôle ou de workflows personnalisés, umzug est une alternative populaire.

Pour installer et initialiser l'interface en ligne de commande (CLI) dans votre projet, exécutez :

npm install --save-dev sequelize-cli

npx sequelize-cli init

Cela crée les dossiers suivants :

config/        # Stores database configuration files
models/        # Stores your Sequelize model definitions
migrations/    # Stores migration files
seeders/       # Stores seeder files for inserting initial data

Vous devriez ajouter ces dossiers au contrôle de version (sauf les fichiers contenant des informations d’identification). Cela garantit que chaque environnement applique de manière cohérente les mêmes modifications de base de données.

Créer et exécuter des migrations

Pour créer un nouveau fichier de migration, utilisez la commande suivante :

npx sequelize-cli migration:generate --name add-isbn-to-books

Cela crée un fichier horodaté dans le dossier migrations/. Ouvrez le fichier et définissez ce qui doit se passer lorsque vous appliquez la migration (up) et comment l'annuler (down) :

module.exports = {
  async up(queryInterface, Sequelize) {
    await queryInterface.addColumn('books', 'isbn', {
      type: Sequelize.STRING
    });
  },
  async down(queryInterface) {
    await queryInterface.removeColumn('books', 'isbn');
  }
};
  • La fonction up applique la migration (ajoute la colonne).
  • La fonction down inverse la modification (supprime la colonne).

Exécutez la migration en utilisant :

npx sequelize-cli db:migrate

Pour revenir à la dernière migration, utilisez :

npx sequelize-cli db:migrate:undo

Vous pouvez également annuler toutes les migrations en développement en exécutant :

npx sequelize-cli db:migrate:undo:all

Écrire des migrations sécuritaires et idempotentes

Suivez ces règles lors de la création de migrations :

  • Encapsulez les opérations dans des transactions lorsque la base de données le permet. Cela garantit que la migration réussit entièrement ou qu'elle est complètement annulée en cas d'erreur.

    module.exports = {
      async up(queryInterface, Sequelize) {
        return queryInterface.sequelize.transaction(async (t) => {
          await queryInterface.addColumn('books', 'isbn', {
            type: Sequelize.STRING
          }, { transaction: t });
        });
      },
      async down(queryInterface) {
        return queryInterface.sequelize.transaction(async (t) => {
          await queryInterface.removeColumn('books', 'isbn', { transaction: t });
        });
      }
    };
  • Gardez les migrations petites et ciblées. Chaque migration devrait effectuer un seul changement logique (ajouter une colonne, créer un index, renommer une colonne). Les petites migrations sont plus faciles à examiner, à tester et à annuler.

  • Soyez défensif et idempotent. Lorsque c'est approprié, vérifiez l'existence pour éviter les échecs dans des environnements avec dérive. Utilisez queryInterface.describeTable() ou des requêtes brutes pour protéger les opérations.

  • Évitez les opérations destructrices en une seule étape. Par exemple, pour supprimer une colonne en toute sécurité, ajoutez d’abord la nouvelle colonne, remplissez-la avec les données existantes, redirigez les lectures vers la nouvelle colonne dans le code de l’application, puis supprimez l’ancienne colonne lors d’une migration ultérieure.

Migrations de longue durée et sans interruption

Les grandes tables et les opérations d'index peuvent provoquer des verrous. Utilisez ces stratégies pour en minimiser l'impact :

  1. Ajoutez d'abord les colonnes pouvant contenir des valeurs nulles. L'ajout d'une colonne nullable est généralement rapide. Évitez d'ajouter des colonnes avec des valeurs par défaut non triviaux qui déclenchent une réécriture complète de la table.
  2. Remplir en petites étapes. Mettez en place un processus en arrière-plan pour remplir les nouvelles colonnes par morceaux plutôt que d'exécuter un seul UPDATE massif.
  3. Créer des index sans verrous exclusifs lorsque possible. Utilisez les fonctionnalités spécifiques à la base de données (par exemple, CONCURRENTLY dans Postgres et le DDL en ligne récent de MySQL) via SQL brut dans les migrations.
  4. Déploiements avec indicateurs de fonctionnalités. Déployez le code de l'application qui lit la nouvelle colonne derrière un indicateur de fonctionnalité. Une fois le remplissage arrière terminé et les tests réussis, activez l'indicateur, puis supprimez ultérieurement l'ancien champ.

Restaurations et correctifs d’urgence

  • Fournir une migration down. Chaque up devrait avoir un down inversé qui documente comment revenir sur le changement. Même si vous utilisez rarement down en production, cela sert de plan de retour clair.
  • Prévoir des plans de retour en arrière d'urgence. Si une migration cause des problèmes, soyez prêt à revenir sur le code de l'application en priorité et à restaurer la base de données à partir des sauvegardes si nécessaire. Testez régulièrement les procédures de restauration.

Initialisation des données et modifications du schéma

  • Gardez les semences minimales et stables. Utilisez les semences pour les lignes de recherche ou de configuration essentielles. Évitez de semer de gros volumes de données commerciales via les migrations.
  • Séparez les données initiales des migrations structurelles. Si vous devez remplir des données qui dépendent d'un changement de schéma, privilégiez un seeder supplémentaire afin que les responsabilités restent claires.

CI, révision de code et automatisation du déploiement

  • Exécutez les migrations d’abord en CI et en environnement de préproduction. Ajoutez une étape CI qui exécute les migrations sur une base de données éphémère pour détecter les erreurs tôt.

  • Traitez les migrations comme du code. Analysez et révisez les fichiers de migration dans les demandes de tirage. Incluez des vérifications automatisées lorsque c'est possible.

  • Automatisez l'exécution des migrations lors des déploiements. Assurez-vous que les migrations s'exécutent avant que les processus de l'application n'acceptent le trafic. Exemple de scripts package.json :

    "scripts": {
      "migrate": "npx sequelize-cli db:migrate",
      "start": "node server.js",
      "deploy": "npm run migrate && npm run start"
    }

Dans les déploiements conteneurisés, exécutez les migrations en tant que conteneur d'initialisation ou dans un hook de déploiement afin que les processus web ne démarrent qu'après la fin des migrations.

Test des migrations

  • Testez les deux directions. Exécutez db:migrate puis db:migrate:undo:all localement et dans l'intégration continue pour valider que up et down fonctionnent tous les deux.
  • Utilisez des bases de données jetables pour les tests. Privilégiez les bases de données éphémères ou les conteneurs pour des exécutions de tests déterministes.

Suivre ces meilleures pratiques garantit que vos migrations Sequelize restent fiables, traçables et sécuritaires pour les projets à long terme.

Dépannage et conseils de performance

Même avec une configuration solide, les applications Sequelize peuvent parfois rencontrer des problèmes inattendus tels que des erreurs de connexion à la base de données, des échecs de migration, des requêtes lentes, des interblocages ou des conflits de concurrence. Cette section fournit des instructions étape par étape pour identifier et résoudre ces problèmes, ainsi que des bonnes pratiques de performance qui vous aident à maintenir votre application stable et efficace.

Dépannage des problèmes courants

1. Erreurs de connexion

Problème : Sequelize ne peut pas se connecter à la base de données MySQL et génère des erreurs telles que SequelizeConnectionError: connect ECONNREFUSED, Accès refusé pour l’utilisateur, ou SequelizeHostNotFoundError.

Comment réparer :

Commencez par confirmer que vos informations de connexion sont correctes. Vérifiez attentivement les valeurs dans votre fichier .env, y compris le nom de la base de données, le nom d'utilisateur, le mot de passe, l'hôte et le port. Même une petite faute de frappe ou un espace en trop peut empêcher Sequelize de se connecter.

Ensuite, assurez-vous que votre serveur MySQL est en fonctionnement et à l'écoute sur le port par défaut (3306). Vous pouvez tester la connectivité en vous connectant manuellement :

mysql -u your_username -p -h your_host -P 3306

Si cette commande échoue, le problème provient probablement de votre service MySQL ou de vos identifiants.

Si vous utilisez le package dotenv, assurez-vous que require('dotenv').config() apparaît avant l'initialisation de Sequelize. Cette étape garantit que toutes les variables d'environnement sont disponibles au moment de l'exécution.

Enfin, pour les connexions à distance, vérifiez vos paramètres de pare-feu ou vos règles de groupe de sécurité. Le serveur MySQL doit accepter les connexions entrantes depuis l’hôte de votre application.

2. Échecs de migration

Problème : Vous exécutez npx sequelize-cli db:migrate ou sequelize.sync() et rencontrez des erreurs telles que des tables manquantes, des colonnes dupliquées ou des incohérences dans le schéma.

Comment réparer :

Les problèmes de migration surviennent généralement lorsque les fichiers sont exécutés dans le mauvais ordre ou contiennent des erreurs dans les opérations SQL. Sequelize traite les migrations en fonction des horodatages dans les noms de fichiers, donc assurez-vous que les fichiers sont correctement nommés.

Si une migration échoue, examinez le message d'erreur en détail. Ensuite, revenez en toute sécurité à la dernière migration en utilisant :

npx sequelize-cli db:migrate:undo

Après avoir corrigé le fichier de migration, relancez-le.

Enveloppez toujours votre logique de migration à l'intérieur d'une transaction pour garantir l'atomicité : soit toutes les modifications réussissent, soit aucune ne le fait. Avant d'exécuter les migrations en production, testez-les minutieusement sur une base de données de préproduction ou locale pour en confirmer l'exactitude.

3. Problèmes de synchronisation du modèle

Problème : Vos modèles Sequelize et votre schéma de base de données ne correspondent pas, ce qui entraîne des colonnes manquantes, des types de données incorrects ou des tables obsolètes.

Comment réparer :

Évitez d'utiliser sequelize.sync({ force: true }) en production, car cela supprimera et recréera les tables, entraînant une perte de données irréversible. De même, utilisez sequelize.sync({ alter: true }) avec prudence ; cela peut modifier les schémas de manière imprévisible.

Préférez plutôt des migrations explicites qui définissent clairement chaque changement de schéma. Pour comparer vos modèles avec la structure réelle de la base de données, utilisez :

await queryInterface.describeTable('tableName');

Cela aide à identifier les incohérences avant qu'elles n'affectent la production.

4. Erreurs de requête et de délai d'attente

Problème : Vous voyez des erreurs liées au dépassement de délai telles que Délai de connexion dépassé ou Trop de connexions.

Comment réparer :

Les problèmes de délai d'attente sont souvent causés par des pools de connexions épuisés ou des connexions non fermées. Assurez-vous que toutes les requêtes Sequelize sont correctement attendues afin que les connexions retournent au pool après utilisation.

Vous pouvez également ajuster la configuration du pool de Sequelize pour mieux convenir à la charge de travail de votre application :

const sequelize = new Sequelize(DB_NAME, DB_USER, DB_PASSWORD, {
  host: DB_HOST,
  dialect: 'mysql',
  pool: {
    max: 10,
    min: 0,
    acquire: 30000,
    idle: 10000
  }
});

Si votre application gère de nombreux utilisateurs simultanés, envisagez d'augmenter le paramètre max_connections de MySQL dans la configuration de votre base de données. Surveillez les connexions actives en utilisant SHOW PROCESSLIST; dans MySQL pour détecter les fuites ou les sessions inutilisées.

5. Performance lente des requêtes

Problème : Certaines requêtes prennent beaucoup plus de temps que prévu, ce qui cause des réponses API lentes ou une latence élevée.

Comment diagnostiquer :

Commencez par identifier quelles requêtes sont lentes. Vous pouvez enregistrer toutes les requêtes exécutées par Sequelize en activant temporairement la journalisation :

const sequelize = new Sequelize(..., { logging: console.log });

Ensuite, utilisez l'instruction EXPLAIN de MySQL pour comprendre comment la base de données exécute la requête :

EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';

Si le résultat montre un « balayage complet de table », la requête manque d’un index.

Vous pouvez également activer le journal des requêtes lentes de MySQL pour capturer automatiquement les opérations lentes :

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

Comment réparer :

Ajoutez des index aux colonnes souvent interrogées (surtout celles utilisées dans les clauses WHERE, JOIN et ORDER BY). Évitez SELECT *, récupérez plutôt explicitement uniquement les champs dont vous avez besoin en utilisant l'option attributes.

Si Sequelize génère des requêtes inefficaces avec des jointures inutiles, réécrivez la requête en utilisant du SQL brut. Pour de grands rapports ou des requêtes analytiques, divisez-les en étapes plus petites ou utilisez des outils de mise en cache comme Redis pour stocker les résultats répétitifs.

6. Verrous mutuels et erreurs de concurrence

Problème : Vous recevez des erreurs telles que Deadlock trouvé lors de la tentative d’obtention du verrou ; essayez de relancer la transaction, surtout en cas de forte charge.

Comment diagnostiquer :

Vérifiez l'état d'InnoDB dans MySQL pour identifier quelles requêtes ou quels tableaux étaient impliqués :

SHOW ENGINE INNODB STATUS\G

Cette sortie comprend des informations détaillées sur les blocages récents et les attentes de verrouillage. Vous pouvez également activer les journaux généraux ou de transactions pour examiner la séquence de requêtes menant au problème.

Comment réparer :

Les interblocages se produisent lorsque deux transactions ou plus attendent les verrous de l’autre. Pour les minimiser :

  1. Gardez les transactions aussi courtes que possible. Ne faites pas appel à des services externes ni n'effectuez de calculs lourds pendant que vous maintenez un verrou.

  2. Accédez toujours aux tables et aux lignes dans le même ordre lors des transactions pour éviter les attentes circulaires.

  3. Pour les interblocages récurrents, mettez en place un simple mécanisme de réessai avec un recul exponentiel :

    async function runWithRetry(fn, retries = 3) {
      for (let i = 0; i < retries; i++) {
        try {
          return await fn();
        } catch (err) {
          if (isDeadlockError(err) && i < retries - 1) {
            await sleep(100 * (i + 1));
            continue;
          }
          throw err;
        }
      }
    }
  4. Utilisez des verrous au niveau des lignes (SELECT ... FOR UPDATE) seulement lorsque c'est nécessaire, et libérez-les rapidement.

  5. Divisez les grosses transactions en plus petits lots pour réduire les conflits.

  6. Ajustez les niveaux d’isolation des transactions si approprié, mais seulement après avoir confirmé que cela n’affectera pas la cohérence des données.

Conseils d'optimisation des performances

Les problèmes de performance proviennent souvent de requêtes inefficaces, d'index manquants ou d'une mauvaise gestion des connexions. Voici quelques conseils pratiques pour améliorer les performances de Sequelize sans compromettre la clarté ou la maintenabilité.

1. Optimiser les requêtes

Rédigez des requêtes qui demandent uniquement ce qui est nécessaire. Récupérer des données inutiles augmente l’utilisation de la mémoire et ralentit les réponses. Utilisez attributes pour limiter les champs :

const users = await User.findAll({ attributes: ['id', 'name'] });

Filtrez toujours les résultats en utilisant des colonnes indexées et évitez autant que possible les recherches larges comme LIKE '%term%'.

2. Utiliser les index de manière efficace

Les index rendent les recherches plus rapides, mais peuvent ralentir les écritures. Ajoutez des index uniquement là où ils offrent des avantages mesurables. Analysez les performances des requêtes avec EXPLAIN de MySQL pour vérifier les améliorations avant et après l’ajout d’un index :

await queryInterface.addIndex('users', ['email']);

3. Ajuster le pool de connexions

Le pool de connexions détermine combien de connexions à la base de données Sequelize maintient.

  • Pour les petites applications, 5 à 10 connexions sont généralement suffisantes.
  • Pour les systèmes à forte charge, augmentez la taille du pool et ajustez les délais d’attente pour éviter les goulets d’étranglement des connexions. Surveillez régulièrement l’utilisation en utilisant SHOW PROCESSLIST; de MySQL.

4. Activer la journalisation et la surveillance

Activez temporairement la journalisation de Sequelize pour examiner les requêtes SQL. Utilisez le journal des requêtes lentes de MySQL pour identifier les requêtes qui dépassent votre seuil de performance. Pour le suivi en temps réel, intégrez des outils de surveillance tels que PM2, New Relic ou Datadog pour mesurer la latence et l'utilisation des ressources.

5. Mettre en cache les requêtes fréquentes

Mettez en cache les résultats pour les données qui ne changent pas souvent. Des outils comme Redis ou Memcached réduisent les accès à la base de données et améliorent les temps de réponse, surtout pour les charges de travail principalement en lecture.

6. Paginer les résultats

Les grands ensembles de résultats consomment beaucoup de mémoire. Paginez toujours les requêtes dans les API :

const users = await User.findAll({ limit: 50, offset: 100 });

La pagination permet de garder les réponses légères et cohérentes sous charge.

7. Utiliser des requêtes brutes pour les opérations complexes

Si l'abstraction de Sequelize génère du SQL inefficace pour un cas d'utilisation spécifique, exécutez une requête SQL brute directement :

const [results] = await sequelize.query('SELECT name FROM users WHERE active = 1');

Utilisez les requêtes brutes avec parcimonie et documentez-les clairement pour la maintenance future.

FAQ

1. Qu'est-ce que Sequelize dans Node.js ?

Sequelize est un mappage objet-relationnel (ORM) pour Node.js qui offre une API de haut niveau basée sur JavaScript pour travailler avec des bases de données SQL telles que MySQL, PostgreSQL, SQLite et MariaDB. Au lieu d'écrire du SQL brut pour chaque opération, vous définissez des modèles qui correspondent aux tables de la base de données et utilisez les méthodes des modèles pour insérer, interroger, mettre à jour et supprimer des enregistrements. Sequelize prend également en charge les associations (relations entre les modèles), les transactions, les migrations et les requêtes SQL brutes lorsque vous avez besoin d'accéder directement au SQL.

2. Comment puis-je connecter Sequelize à MySQL ?

Pour connecter Sequelize à MySQL, vous créez une instance de Sequelize avec vos informations d'identification de base de données et vos options. Dans un fichier Node.js, vous devez importer Sequelize, charger éventuellement les variables d'environnement, puis l'instancier avec le nom de la base de données, le nom d'utilisateur, le mot de passe et les options d'hôte/dialecte. Par exemple :

require('dotenv').config();
const { Sequelize } = require('sequelize');

const sequelize = new Sequelize(
  process.env.DB_NAME,
  process.env.DB_USER,
  process.env.DB_PASSWORD,
  {
    host: process.env.DB_HOST || 'localhost',
    dialect: 'mysql'
  }
);

await sequelize.authenticate();
console.log('Connected to the database successfully!');

Gardez toujours les informations d'identification en dehors du contrôle de version (par exemple, dans un fichier .env ou un gestionnaire de secrets).

3. Comment puis-je créer et synchroniser des modèles dans Sequelize ?

Dans Sequelize, les modèles représentent des tables. Vous les définissez en utilisant sequelize.define() ou en étendant la classe Model. Après cela, vous pouvez les synchroniser avec votre base de données afin que Sequelize crée automatiquement les tables (idéal pour le développement). Voici un petit exemple :

const { DataTypes } = require('sequelize');

const Book = sequelize.define('Book', {
  title: { type: DataTypes.STRING, allowNull: false },
  author: { type: DataTypes.STRING, allowNull: false },
  releaseDate: DataTypes.DATEONLY
});

// Create the table if it does not exist
await sequelize.sync();

Pour les environnements de production, il est préférable d'utiliser des migrations afin d'avoir un contrôle de version sur les modifications du schéma.

4. Comment utiliser Sequelize avec Express ?

Dans une application Express, initialisez Sequelize dans un fichier séparé (comme db.js), exportez l’instance connectée et les modèles, puis importez-les partout où vous en avez besoin dans vos gestionnaires de routes ou vos contrôleurs. Utilisez async/await dans les gestionnaires de routes pour appeler les méthodes des modèles Sequelize, et gérez les erreurs avec try/catch ou un middleware d’erreur. Voici à quoi cela ressemble en pratique :

// db.js
const sequelize = new Sequelize(...);
const User = sequelize.define(...);
module.exports = { sequelize, User };

// app.js
const { User } = require('./db');
app.get('/users', async (req, res) => {
  const users = await User.findAll();
  res.json(users);
});

Assurez-vous d'exécuter les migrations ou d'appeler sequelize.sync() avant de démarrer votre serveur Express afin que vos tables de base de données existent lorsque les requêtes arrivent.

5. Quelle est la différence entre Sequelize et les requêtes SQL brutes ?

Sequelize vous permet d'écrire la logique de base de données en JavaScript, tandis que SQL brut signifie écrire les instructions SQL directement. Sequelize rend votre code plus propre et plus facile à maintenir, car vous pouvez définir des modèles et des relations une seule fois et les utiliser dans toute votre application. Le SQL brut vous offre plus de contrôle et peut parfois être plus rapide pour des requêtes très complexes.

En bref, utilisez Sequelize pour la plupart des tâches quotidiennes, et utilisez le SQL brut lorsque vous devez optimiser les performances ou exécuter quelque chose que Sequelize ne peut pas exprimer facilement.

6. Comment puis-je définir des associations dans Sequelize ?

Sequelize facilite la description des relations entre les tables. Vous pouvez configurer des relations un-à-un, un-à-plusieurs ou plusieurs-à-plusieurs en utilisant des méthodes simples comme hasOne, hasMany et belongsToMany. Par exemple :

// One to one
User.hasOne(Profile);
Profile.belongsTo(User);

// One to many
Author.hasMany(Book);
Book.belongsTo(Author);

// Many to many
Student.belongsToMany(Course, { through: 'StudentCourse' });
Course.belongsToMany(Student, { through: 'StudentCourse' });

Une fois défini, vous pouvez utiliser include dans vos requêtes pour récupérer automatiquement les données associées.

7. Comment gérer les migrations et les changements de schéma ?

En développement, vous pouvez utiliser sequelize.sync() pour créer rapidement des tables. Mais en production, vous devriez toujours utiliser des migrations. Les migrations sont des fichiers versionnés qui décrivent comment le schéma de votre base de données évolue au fil du temps, de la même manière que les commits Git suivent les modifications du code.

Vous pouvez créer une migration avec le CLI de Sequelize :

npx sequelize-cli migration:generate --name add-users-table

Chaque migration possède des méthodes up et down, l'une pour appliquer le changement et l'autre pour l'annuler. Exécutez vos migrations avec :

npx sequelize-cli db:migrate

De cette façon, tout le monde dans votre équipe (et chaque déploiement) garde la même structure de base de données.

Utilisez sequelize.sync() pour des expérimentations rapides, mais privilégiez les migrations une fois votre application en production, car elles sont plus sûres, réversibles et plus faciles à suivre.

Conclusion

Dans ce tutoriel, vous configurez Sequelize avec Node.js et MySQL pour gérer les données à travers des modèles, des associations et des migrations. Vous avez également sécurisé vos identifiants de base de données avec des variables d'environnement et exploré les meilleures pratiques pour le dépannage et l'optimisation des performances.

Avec ces étapes, vous avez maintenant une base solide pour créer et maintenir des applications Node.js sécurisées et efficaces utilisant Sequelize et MySQL pour la gestion des données.

Pour en savoir plus sur Sequelize, consultez la documentation du produit. Pour d'autres tutoriels Node.js de ce type, consultez les articles suivants :

  • Comment configurer un serveur GraphQL dans Node.js avec Apollo Server et Sequelize
  • Comment implémenter la mise en cache dans Node.js en utilisant Redis
  • Comment créer un serveur Web en Node.js avec le module HTTP

Merci de apprendre avec la communauté Linux-Console.net.