Recherche de site Web

Comment utiliser Sequelize avec Node.js et MySQL


L'auteur a choisi le Fonds pour l'Internet Ouvert/la Liberté d'Expression pour recevoir un don dans le cadre du programme Écrire pour des Dons.

Introduction

Sequelize est un ORM basé sur Node.js qui facilite le travail avec les bases de données MySQL, MariaDB, SQLite, PostgreSQL, et plus encore. Un ORM (Object Relational Mapper) effectue des fonctions telles que la gestion des enregistrements de la base de données en représentant les données sous forme d'objets. Sequelize dispose d’un puissant mécanisme de migration 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 anticipé, les associations, les transactions et les migrations de bases de données tout en réduisant le temps de développement et en prévenant 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. Puis, 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 également la configuration sécurisée de la base de données en utilisant des variables d’environnement, la gestion en toute sécurité des modifications de schéma via des migrations, et les bonnes pratiques pour le dépannage et l’optimisation des performances.

Déployez vos applications frontend depuis GitHub en utilisant la plateforme App de Linux-Console.net. Laissez Linux-Console.net se concentrer sur la mise à l'échelle de votre application.

Points clés :

  • Sequelize fournit un moyen facile d'interagir avec MySQL en utilisant des modèles au lieu du SQL brut, rendant les opérations de base de données plus simples et mieux organisées.
  • Stocker les informations d'identification de la base de données dans des variables d'environnement permet de garder les informations sensibles sécurisées et hors du contrôle de version.
  • Utiliser des migrations pour les mises à jour du schéma est plus sûr que de se fier à 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 encore être utilisées lorsque vous avez besoin de plus de contrôle ou de meilleures performances pour des opérations de base de données complexes.
  • La plupart des problèmes courants de 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 fréquemment consultées.
  • Tester les migrations, ajouter la gestion des erreurs et surveiller les performances de la base de données aide à 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 exécutant 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

À cette étape, vous allez installer Sequelize et créer la connexion à votre base de données MySQL. Pour ce faire, vous allez d'abord 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 de projet. Dans cet exemple, vous pouvez utiliser hello-world. Une fois le dossier créé, naviguez vers le dossier 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, vous serez invité à 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 ressemblera à ce qui 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 server.js vide à l'intérieur du dossier du projet :

touch server.js

Après avoir suivi les étapes précédentes, votre structure de dossiers finale 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 comprenait 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 travaillez à distance, vous pouvez utiliser votre outil préféré. Si vous utilisez une instance MySQL locale, vous pouvez utiliser la commande suivante, en remplaçant your_username par votre nom d'utilisateur MySQL :

mysql -u your_username -p

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

Le serveur MySQL vous demandera votre mot de passe de 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 la base de données de votre choix. Comme Sequelize fournit uniquement des fonctionnalités ORM, il n’inclut pas de pilotes de base de données intégrés. Par conséquent, vous devrez installer les pilotes selon votre préférence. Pour ce faire, naviguez dans le répertoire du projet en utilisant le terminal et installez le pilote MySQL dans le projet en utilisant la commande suivante :

npm install --save mysql2

Dans ce cas, vous utilisez le pilote pour MySQL.

Remarque : Comme 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 cette manière :

  • 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 connectivité à 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 informations d'identification 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é, donc vous devrez 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 en conséquence avec 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 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 teste si les informations d'identification fournies sont correctes. 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 tandis qu'un bloc de code asynchrone est en cours d'exécution. De plus, après l'exécution réussie du bloc de code asynchrone, il retourne une promise, qui est la valeur retourné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 lançant 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 allez travailler avec des 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 choses pour Sequelize, telles 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 des 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 pour 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 ajoutez 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 vous référer à 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 identifiants 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 indiqué :

...

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 stocke les enregistrements de livres selon le title, le author, le release_date et le subject.

Dans ce code, allowNull montre 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 allez ajouter 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 vérifierez 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 du modèle en passant des paramètres force pour forcer la création d'une nouvelle table si elle n'existe pas, ou bien utiliser une table existante. Voici quelques exemples, qui peuvent vous être utiles lorsque vous travaillez avec 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, Book table created successfully!. 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, changez pour la base de données 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 été réussie. 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 des méthodes intégrées. Vous avez également utilisé des types de données pris en charge par Sequelize pour définir votre modèle. Ensuite, vous travaillerez avec des requêtes de modèle de base.

Utiliser 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 suivantes mises en évidence :

...

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 aux modèles créés précédemment. 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 passer les données que vous devez ajouter à la base de données sous forme d'objet. La section de code mise en évidence insérera une nouvelle entrée dans votre table books existante. Dans cet exemple, vous ajoutez Clean Code de Robert Cecil Martin, qui 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 inséré un nouvel enregistrement dans le 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élection de 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 précédente méthode Book.create(). 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 à 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 d'un objet tableau. Vous avez utilisé avec succès la méthode Sequelize findAll() pour retourner toutes les données des livres depuis 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 de données. Pour ce tutoriel, vous allez obtenir un livre par un ID d'enregistrement spécifique de la base de données en utilisant la méthode findOne().

Pour ce faire, ouvrez book.controller.js pour la modification, 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 l'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 capturer des données conditionnelles.

Supprimer 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 surlignées suivantes :

...
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.

L'enregistrement a été supprimé.

Dans cette étape, vous avez expérimenté avec votre modèle de base de données et les requêtes sur le 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 capable de définir et de travailler avec une variété de relations en utilisant les modèles Sequelize.

Créer des associations en utilisant Sequelize

Dans cette étape, vous utiliserez les types d'association standard que Sequelize prend en charge : one-to-one, one-to-many et many-to-many. 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 many-to-many : Une relation many-to-many 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 quelques 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 dans Installer et Configurer 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 des associations de bases 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 voulez obtenir les détails d'un étudiant ainsi que son niveau de classe. Comme un étudiant ne peut avoir qu'un seul niveau de classe, 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 sur la table source, tandis que hasOne() l'ajoutera sur 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 de 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 appelé one_to_one.js. Comme vous l'avez fait dans la section précédente, Connexion à la base de données MySQL, incluez les lignes pour créer une connexion à la base de données et authentifier votre utilisateur MySQL en haut du fichier. Assurez-vous de mettre à jour les identifiants 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 Courses.

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 indiqué :

...
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 classe associés en passant le paramètre include à l'intérieur de la méthode findAll().

Puisque vous devez obtenir le niveau scolaire de l'élève, vous passerez Grade comme modèle. Dans la méthode sequelize.sync(), ajoutez les lignes mises en évidence 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

La sortie sera longue, et vous verrez les données de tous les élèves avec les niveaux de classe. Voici un extrait de la sortie 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 étendue ou non. Si c'est une vue étendue, elle affiche l'objet grade étendu en tant que sortie.

Dans cette section, vous avez créé une relation un-à-un en utilisant l'appel de méthode Student.belongsTo(Grade); et 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 en classe de 9. 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

La sortie ressemblera à ce qui suit. Elle sera assez longue, mais tous les élèves de la classe 9 seront retourné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 mettre cela en œuvre 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 mises en évidence 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. Ensuite, 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 indiqué :

...
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 à l'intérieur de 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 distinctes qui indiquent chaque cours. Par exemple, id : 1 est lié à 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

Dans 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 depuis la base de données. Vous avez peut-être remarqué que ces méthodes suivent un modèle spécifique pour rédiger une requête. Cependant, avec l'utilisation de requêtes brutes, vous n'avez pas besoin de vous préoccuper des méthodes et des modèles intégrés de Sequelize. En utilisant vos connaissances en 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éthodologies : 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 étudiantes dans une base de données d'exemple. 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, et déconnectez-vous de MySQL.

Ensuite, vous allez ajouter 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

La sortie sera quelque chose de similaire à ce qui suit. Elle sera assez longue, mais tous les enregistrements des étudiants que vous avez insérés seront retournés comme suit. Notez que puisque le student_id est une valeur UUID (Identificateurs Universellement Uniques) 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 appliquerez 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 un remplacement de 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, car 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 avec 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é en tant que 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 le besoin, vous pouvez sélectionner 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é en utilisant 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 similaire à la suivante :

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 schéma de passage 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 avec 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 le query(), vous indiquez : 'SELECT * FROM students WHERE student_id=:id'. En utilisant la méthode query(), vous transmettez 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 identifiants de 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. L'intégration directe des identifiants expose des informations sensibles telles que les mots de passe et les noms d'hôtes 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 identifiants 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 lors de l'exécution. Au lieu d'intégrer les informations d'identification directement dans votre code, elles sont stockées dans votre système ou dans un fichier de configuration séparé qui n'est jamais enregistré dans le contrôle de version. Cette approche offre plusieurs avantages :

  • Sécurité : Vos identifiants restent cachés 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 besoin de modifier qu'un seul fichier ou une seule variable.
  • Conformité : Cette pratique est conforme à la méthodologie Twelve-Factor App, qui favorise la gestion de la configuration spécifique à l'environnement pour des applications évolutives.

Configuration des variables d'environnement

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

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

npm install dotenv

Après l'installation, créez un nouveau fichier nommé .env dans le répertoire racine de votre projet. Ce fichier contiendra toutes les données sensibles nécessaires pour 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écurisation du 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 le saute lors de la validation des modifications :

.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 local .env.

Mise à jour de votre configuration Sequelize

Maintenant que vous avez défini les variables d'environnement, mettez à jour votre initialisation de 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 renforce 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 est en cours d'exécution, 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 fonctionne et est 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 de variables d'environnement est une première étape importante vers une gestion de configuration sécurisée. Pour garantir une sécurité et une fiabilité continues, considérez les meilleures pratiques supplémentaires suivantes :

  • Utilisez des configurations séparées par environnement : Maintenez différents fichiers .env pour development, staging et production. Vous pouvez les nommer .env.development, .env.staging et .env.production.
  • Exploitez les gestionnaires de secrets : Pour les environnements de production, utilisez des gestionnaires de secrets au lieu de stocker les 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 d'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 pour qu'ils disposent des privilèges les plus restreints nécessaires à l'application.

En implémentant des variables d'environnement et en suivant ces pratiques, vous garantissez que votre application Sequelize maintient à la fois flexibilité et sécurité à travers les environnements de développement et de production.

Meilleures pratiques pour la migration et la 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 servent des objectifs différents et comportent des compromis. Comprenons quand utiliser chacune, comment écrire des migrations sûres et comment les exécuter de manière fiable en développement et en production.

Migrations vs sequelize.sync() — quand utiliser lequel

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

  • 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 automatiquement les tables. 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écurisées sans possibilité de retour en arrière contrôlé.

Outils : sequelize-cli et exécuteurs de migrations

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

Pour installer et initialiser le 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 (à l'exception de tout fichier contenant des identifiants). Cela garantit que chaque environnement exécute les mêmes modifications de base de données de manière cohérente.

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 le changement (supprime la colonne).

Exécutez la migration en utilisant :

npx sequelize-cli db:migrate

Pour annuler 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ûres et idempotentes

Suivez ces règles lors de la rédaction des migrations :

  • Encapsulez les opérations dans des transactions lorsque la base de données le prend en charge. Cela garantit que la migration réussit complètement ou 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 doit 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 cela 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 sécuriser les opérations.

  • Évitez les opérations destructrices en une seule étape. Par exemple, pour supprimer une colonne en toute sécurité, ajoutez la nouvelle colonne, remplissez-la en arrière, redirigez les lectures vers la nouvelle colonne dans le code de l'application, puis seulement ensuite supprimez l'ancienne colonne dans une migration ultérieure.

Migrations de longue durée et sans interruption

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

  1. Ajoutez d'abord les colonnes pouvant être nulles. Ajouter une colonne pouvant être nulle est généralement rapide. Évitez d'ajouter des colonnes avec des valeurs par défaut non triviales qui déclenchent une réécriture complète de la table.
  2. Remplissez en petites quantités. Mettez en place un travail en arrière-plan pour remplir les nouvelles colonnes par morceaux plutôt que d'exécuter une seule UPDATE massive.
  3. Créez des index sans verrous exclusifs lorsque cela est possible. Utilisez les fonctionnalités spécifiques à la base de données (par exemple, CONCURRENTLY de Postgres et le DDL en ligne récent de MySQL) via SQL brut dans les migrations.
  4. Déploiements avec drapeau de fonctionnalité. Déployez le code de l'application qui lit la nouvelle colonne derrière un drapeau de fonctionnalité. Après que le remplissage arrière est terminé et que les tests réussissent, activez le drapeau et supprimez ensuite l'ancien champ.

Rétrogradations et correctifs d'urgence

  • Fournissez une migration down. Chaque up doit avoir un down inverse qui documente comment annuler le changement. Même si vous exécutez rarement le down en production, il sert de plan de retour clair.
  • Planifiez des retours en arrière d'urgence. Si une migration cause des problèmes, soyez prêt à revenir en arrière sur le code de l'application en premier et à restaurer la base de données à partir des sauvegardes si nécessaire. Testez régulièrement les procédures de restauration.

Importation de données et modifications de schéma

  • Gardez les seeders minimaux et stables. Utilisez les seeders pour les lignes de recherche ou de configuration essentielles. Évitez de semer de grands volumes de données métier via les migrations.
  • Séparez les seeds des migrations structurelles. Si vous devez remplir des données qui dépendent d'un changement de schéma, préférez un seeder ultérieur afin que les responsabilités restent claires.

CI, revue de code et automatisation du déploiement

  • Exécutez les migrations en CI et en staging d'abord. 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 pull requests. Incluez des vérifications automatisées autant que possible.

  • Automatiser l'exécution des migrations lors des déploiements. Assurez-vous que les migrations s'exécutent avant que les processus de l'application 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 CI pour valider que up et down fonctionnent tous les deux.
  • Utilisez des bases de données jetables pour les tests. Préférez des bases de données éphémères ou des conteneurs pour des exécutions de tests déterministes.

Suivre ces meilleures pratiques garantit que vos migrations Sequelize restent fiables, traçables et sûres pour des 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.

Résolution 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 vérifier que vos informations de connexion sont correctes. Revérifiez 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 fonctionne et é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 paquet dotenv, assurez-vous que require('dotenv').config() apparaît avant que Sequelize ne soit initialisé. Cette étape garantit que toutes les variables d'environnement sont disponibles au moment de l'exécution.

Enfin, pour les connexions à distance, vérifiez les paramètres de votre pare-feu ou les règles du 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 en double ou des incompatibilités de 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, il est donc important de vérifier 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 dans une transaction pour garantir l'atomicité : soit toutes les modifications réussissent, soit aucune ne réussit. Avant d'exécuter des migrations en production, testez-les minutieusement sur une base de données de préproduction ou locale pour confirmer leur exactitude.

3. Problèmes de synchronisation du modèle

Problème : Vos modèles Sequelize et le schéma de la 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, provoquant une perte de données irréversible. De même, utilisez sequelize.sync({ alter: true }) avec précaution ; il peut modifier les schémas de manière imprévisible.

Préférez plutôt des migrations explicites qui définissent clairement chaque modification du 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 temps d'attente 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 affiner 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 MySQL max_connections 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 entraîne des réponses API lentes ou une forte latence.

Comment diagnostiquer :

Commencez par identifier quelles requêtes sont lentes. Vous pouvez enregistrer toutes les requêtes que Sequelize exécute 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 la 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 fréquemment interrogées (en particulier 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 les rapports volumineux ou les 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. Verrouillages et erreurs de concurrence

Problème : Vous recevez des erreurs telles que Deadlock found when trying to get lock; try restarting transaction, surtout en cas de forte charge.

Comment diagnostiquer :

Vérifiez le statut d’InnoDB dans MySQL pour identifier quelles requêtes ou tables étaient impliquées :

SHOW ENGINE INNODB STATUS\G

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

Comment réparer :

Les interblocages se produisent lorsque deux transactions ou plus attendent les verrous des autres. Pour les minimiser :

  1. Gardez les transactions aussi courtes que possible. Ne contactez pas de services externes et n'effectuez pas de calculs lourds tout en détenant un verrou.

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

  3. Pour les blocages récurrents, mettez en œuvre un mécanisme de réessai simple avec une rétroaction exponentielle :

    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) uniquement lorsque c'est nécessaire, et libérez-les rapidement.

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

  6. Ajustez les niveaux d'isolation des transactions si nécessaire, 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 sacrifier la clarté ou la maintenabilité.

1. Optimiser les requêtes

Rédigez des requêtes qui ne demandent que 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 les recherches larges comme LIKE '%term%' lorsque c'est possible.

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 apportent 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. Régler 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 de connexion. 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 très axées sur la lecture.

6. Paginer les résultats

Les grands ensembles de résultats consomment beaucoup de mémoire. Paginer systématiquement 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. Utilisez des requêtes brutes pour les opérations complexes

Si l’abstraction de Sequelize entraîne un SQL inefficace pour un cas d’utilisation spécifique, exécutez directement une requête SQL brute :

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.

Questions fréquemment posées

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

Sequelize est un mappeur objet-relationnel (ORM) pour Node.js qui fournit 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 modèles), les transactions, les migrations et les requêtes SQL brutes lorsque vous avez besoin d'un SQL direct.

2. Comment puis-je connecter Sequelize à MySQL ?

Pour connecter Sequelize à MySQL, vous créez une instance de Sequelize avec vos identifiants de base de données et vos options. Dans un fichier Node.js, importez Sequelize, chargez éventuellement les variables d'environnement, puis instanciez-le 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 identifiants en dehors du contrôle de version (par exemple, dans un fichier .env ou un gestionnaire de secrets).

3. Comment 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 les tables automatiquement (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 changements de schéma.

4. Comment puis-je 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, et importez-les là où vous en avez besoin dans vos gestionnaires de routes ou 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 de gestion des erreurs. 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 le SQL brut signifie écrire des 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 fois et les utiliser dans toute votre application. Le SQL brut vous donne 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 liées.

7. Comment gérer les migrations et les modifications 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 votre schéma de base de données change 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 la modification et l'autre pour la 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) conserve la même structure de base de données.

Utilisez sequelize.sync() pour des expériences rapides, mais restez sur les migrations une fois que votre application est en ligne 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 via 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 qui utilisent Sequelize et MySQL pour la gestion des données.

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

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

Merci d'apprendre avec la communauté Linux-Console.net.