Recherche de site Web

Comment utiliser Sequelize avec Node.js et MySQL


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

Introduction

Sequelize est un ORM (Object Relational Mapper) basé sur Node.js qui facilite le travail avec les bases de données MySQL, MariaDB, SQLite, PostgreSQL, et plus encore. Un 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 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 les remplacements de tableaux et d’objets. En plus de ces notions de base, le tutoriel couvre également la configuration sécurisée de la base de données à l’aide de variables d’environnement, la gestion sûre des modifications de schéma via les 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 d'applications Linux-Console.net. Laissez Linux-Console.net se charger de l'évolution de votre application.

Points clés :

  • Sequelize offre un moyen simple d'interagir avec MySQL en utilisant des modèles plutôt que du SQL brut, ce qui rend les opérations sur la 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 sécuriser les informations sensibles et de les garder 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 de relations un-à-un, un-à-plusieurs et plusieurs-à-plusieurs entre les modèles.
  • Les requêtes SQL brutes peuvent toujours être utilisées lorsque vous avez besoin de plus de contrôle ou d'une meilleure performance pour des opérations de base de données complexes.
  • Les problèmes les plus courants avec Sequelize, tels que les échecs de connexion ou les erreurs de migration, peuvent être résolus en vérifiant les variables d'environnement, en consultant les journaux et en utilisant des outils MySQL comme EXPLAIN et SHOW PROCESSLIST.
  • Les performances peuvent être améliorées en optimisant les requêtes, en ajoutant des index appropriés, en ajustant les pools de connexions et en mettant en cache les données fréquemment consultées.
  • Tester les migrations, ajouter la gestion des erreurs et surveiller la performance de la base de données aident à maintenir les applications Sequelize fiables et prêtes pour la production.

Prérequis

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

  • Un environnement de développement 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 Node.js version v24.11.1 et npm version 11.6.2.

Installation et configuration de Sequelize

Dans cette étape, vous allez installer Sequelize et créer la connexion à votre base de données MySQL. Pour ce faire, vous 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.

Installer Sequelize

Commencez par créer un dossier de projet. Dans cet exemple, vous pouvez utiliser hello-world. Une fois le dossier créé, accédez au 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, il vous sera demandé de répondre à quelques questions de configuration. Utilisez la sortie suivante pour votre configuration. Appuyez sur ENTRÉE pour utiliser la valeur par défaut affichée et assurez-vous de définir le point d'entrée principal comme server.js. Cela crée une structure de projet facile à maintenir.

La sortie 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, la structure finale de votre dossier 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 pour vous y connecter.

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

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

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

mysql -u your_username -p

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

Le serveur MySQL vous demandera 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 manuellement un pilote pour la base de données de votre choix. Comme Sequelize ne fournit que des fonctionnalités ORM, il n’inclut pas de pilotes de base de données intégrés. Par conséquent, vous devrez installer des pilotes selon votre préférence. Pour ce faire, naviguez vers 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 la manière suivante :

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

Maintenant que vous disposez d'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'édition 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 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 informations d'identification de votre utilisateur MySQL :

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

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

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

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

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

...

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

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

La plupart des méthodes fournies par Sequelize sont asynchrones. Cela signifie que vous pouvez exécuter des processus dans votre application pendant qu'un bloc de code asynchrone est en cours d'exécution. De plus, après l'exécution réussie d'un bloc de code asynchrone, il renvoie une promesse, qui est la valeur retournée à la fin d'un processus. Par conséquent, dans les blocs de code asynchrone, 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, lancez l'application server.js en exécutant 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.

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

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

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

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

nano book.model.js

De la même manière que l'étape précédente, ajoutez le code Sequelize pour l'initialisation de la base de données avec une nouvelle importation de DataTypes en haut du fichier :

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

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

Ensuite, incluez les lignes que vous avez utilisées précédemment pour créer une connexion à votre base de données MySQL, en mettant à jour vos 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 indique que la valeur de la colonne du modèle ne peut pas être null. De même, si vous devez définir une telle valeur, vous pouvez utiliser defaultValue: "value".

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

...

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

Dans la méthode sync(), vous demandez à Sequelize de faire plusieurs choses avec la base de données. Avec cet appel, Sequelize exécutera automatiquement une requête SQL à la base de données et créera une table, en 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 a été créée avec succès. Sinon, elle retournera une erreur via la méthode catch et l'affichera dans la sortie.

Remarque : Vous pouvez gérer la synchronisation des modèles en passant des paramètres force pour obliger la création d'une nouvelle table si elle n'existe pas, ou utiliser une table existante. Voici quelques exemples, qui peuvent vous être utiles 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, Table des livres créée avec succès !. Vous pouvez le vérifier en consultant votre base de données pour voir la nouvelle table books créée dans la base de données hello_world_db.

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

mysql -u YOUR_USERNAME -p

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

USE hello_world_db;

Et puis exécutez la commande pour afficher les tables :

SHOW TABLES;

Votre résultat 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 les méthodes intégrées. Vous avez également utilisé les types de données pris en charge par Sequelize pour définir votre modèle. Ensuite, vous travaillerez avec des requêtes de 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 conditions, et la suppression.

Insertion d'un nouveau 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 mises en évidence suivantes :

...

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

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

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

Ici, vous insérez un nouvel enregistrement de livre dans le modèle books que vous avez déjà créé en utilisant la méthode sync(), qui permet d'ajouter de nouveaux enregistrements aux modèles déjà créés. Une fois que la méthode sync() s'exécute avec succès, elle déclenche 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 souhaitez 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é catégorisé 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électionner tous les enregistrements

Dans cette section, vous allez sélectionner et obtenir tous les enregistrements de livres de la base de données en utilisant la méthode findAll(). Pour ce faire, ouvrez d'abord book.controller.js et supprimez l'ancienne 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'objet tableau. Vous avez utilisé avec succès la méthode Sequelize findAll() pour renvoyer 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 des données. Pour ce tutoriel, vous allez obtenir un livre à partir d'un ID d'enregistrement spécifique dans la base de données en utilisant la méthode findOne().

Pour ce faire, ouvrez book.controller.js pour l'édition, 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 réussi à utiliser les clauses where pour obtenir des données à partir des modèles Sequelize. Vous pouvez utiliser la clause where dans l'application de base de données pour récupérer des données conditionnelles.

Suppression d'un enregistrement

Pour supprimer un enregistrement spécifique du modèle de base de données, 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 mises en évidence 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 à l'aide de la clause where et supprimé des enregistrements sélectionnés. Avec ces connaissances de Sequelize, vous allez maintenant créer des associations dans Sequelize. Après cela, vous pourrez définir et travailler avec une variété de relations en utilisant les modèles Sequelize.

Créer des associations avec Sequelize

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

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 d'études.

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 les associations de base de données.

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

Dans cette section, vous allez créer une relation un-à-un en utilisant les modèles Sequelize. Imaginez que vous souhaitez obtenir les détails d’un étudiant ainsi que son niveau de 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 dans la table source, tandis que hasOne() l'ajoutera dans la table cible. Dans tous les cas, si les deux relations sont utilisées en même temps, cela fonctionnera comme des relations un-à-un bidirectionnelles dans Sequelize.

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

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

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

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

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

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

Pour le modèle Student, ajoutez le bloc de code suivant dans 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 toutes les données des é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 toutes les données des étudiants avec leurs niveaux de grade. Voici un extrait de la sortie montrant les données des étudiants :

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

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

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

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

Dans cette section, vous allez créer une relation un-à-plusieurs en utilisant les modèles Sequelize. Imaginez que vous souhaitez obtenir tous les étudiants associés à un niveau scolaire sélectionné. Comme 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 élèves d'un niveau scolaire particulier - dans ce cas, tous les élèves de la 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

Le résultat ressemblera à ce qui suit. Il sera assez long, mais tous les élèves de la classe de 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. À titre d'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 surlignées si nécessaire.

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

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

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

Ensuite, vous allez créer les modèles de base de données pour les relations plusieurs-à-plusieurs : Student et Course. Puis, vous allez ajouter 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 en fonction de 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 dans la base de données. Ensuite, créez des relations plusieurs-à-plusieurs en utilisant la méthode belongsToMany() comme montré :

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

Dans la méthode belongsToMany(), vous passez la configuration through avec le nom du modèle comme option de configuration. Dans ce cas, il s'agit de StudentCourse. Il s'agit de 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 le ferez 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 Sciences, 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 dans la base de données. Vous avez peut-être remarqué que ces méthodes suivent un modèle spécifique pour écrire une requête. Cependant, avec l’utilisation des 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 des requêtes SQL, vous pouvez effectuer une variété de requêtes dans Sequelize, allant 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 de deux manières : le remplacement par tableau et le remplacement par objet. Lorsque vous passez des valeurs à la requête SQL, vous pouvez utiliser soit un tableau, soit un objet pour effectuer ce remplacement.

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

Ensuite, vous 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 lancez la connexion à la base de données, créez le modèle et insérez quelques enregistrements d'étudiants dans la nouvelle base de données.

Enregistrez et fermez le fichier.

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

node add_student_records.js

Le résultat sera quelque chose de similaire à ce qui suit. Il sera assez long, 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 (Identifiant Unique Universel) générée automatiquement, elle sera différente selon l'utilisateur.

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

…

Dans la section suivante, vous 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 remplacer un tableau. Avec cette méthode, Sequelize peut exécuter des requêtes SQL brutes ou déjà préparées.

Pour commencer, copiez le contenu du fichier server.js de l'Étape 1, 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 en 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 veillant à 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() avec 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 vos besoins, vous pouvez choisir le type qui convient à votre objectif.

Ce qui suit montre le bloc de code complet. Ici, vous vous connectez à la base de données et récupérez les données de l'étudiant sélectionné 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 de l’student_id sélectionné, vos valeurs de sortie peuvent varier.

Remplacement d'objet

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

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

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

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

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

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

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

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

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

Voici le code complet :

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

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

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

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

Enregistrez et fermez le fichier.

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

node object_raw_query.js

La sortie ressemblera à ce qui suit :

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

En raison de l’student_id sélectionné, vos valeurs de sortie peuvent varier.

À 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 la base de données directement dans votre code. Bien que cette approche fonctionne pour des expérimentations locales, elle n’est pas sécurisée. Intégrer des identifiants en dur 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 à l'exécution. Au lieu d'intégrer directement les informations d'identification dans votre code, elles sont stockées dans votre système ou dans un fichier de configuration séparé qui n'est jamais intégré au 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é : La configuration centralisée permet des mises à jour faciles, si le mot de passe de la base de données change, vous n'avez qu'à modifier un seul fichier ou une seule variable.
  • Conformité : Cette pratique est conforme à la méthodologie Twelve-Factor App, qui favorise la gestion 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 depuis un fichier .env et les injecte dans l'objet process.env de Node.js, les rendant accessibles dans toute votre application.

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

npm install dotenv

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

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

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

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

Sécuriser le fichier .env

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

.env

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

Mise à jour de votre configuration Sequelize

Maintenant que vous avez défini les variables d'environnement, mettez à jour l'initialisation de Sequelize pour les utiliser. Commencez par importer et configurer dotenv en haut de votre fichier serveur principal (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 fonctionne, vous verrez la sortie suivante :

Connection has been established successfully.

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

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

Meilleures pratiques pour une configuration sécurisée

Utiliser des variables d'environnement est une première étape solide vers une gestion de configuration sécurisée. Pour garantir la sécurité et la fiabilité continues, prenez en compte les bonnes pratiques supplémentaires suivantes :

  • Utilisez des configurations séparées pour chaque 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 plutôt que de stocker des secrets dans des fichiers.
  • Valider les variables d'environnement : Utilisez un utilitaire de configuration ou un script de démarrage qui vérifie si les variables requises sont définies avant que l'application ne s'exécute. Cela aide à prévenir les erreurs à l'exécution.
  • Faites tourner régulièrement les identifiants : Mettez à jour les identifiants de la base de données périodiquement 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 uniquement des privilèges nécessaires à l'application.

En mettant en place des variables d'environnement et en suivant ces pratiques, vous vous assurez que votre application Sequelize conserve à la fois flexibilité et sécurité, tant en environnement de développement qu'en 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 ont des objectifs différents et présentent des compromis. Voyons 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 l’un ou l’autre

  • Migrations (recommandées 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, relues dans le cadre du code review, et exécutées dans le cadre de votre processus de déploiement. Utilisez les migrations en production pour toute modification du schéma qui affecte l'intégrité 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 dangereuses sans possibilité de retour en arrière contrôlé.

Outils : sequelize-cli et gestionnaires 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 des fichiers contenant des identifiants). Cela garantit que chaque environnement applique les mêmes modifications de la 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 lors de l'application de 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 annule la modification (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 de migrations :

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

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

  • Soyez défensif et idempotent. Lorsque c’est approprié, vérifiez l’existence pour éviter les échecs dans des environnements avec dérive. Utilisez queryInterface.describeTable() ou des requêtes brutes pour 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, redirigez les lectures vers la nouvelle colonne dans le code de l'application, puis seulement après, supprimez l'ancienne colonne lors d'une migration ultérieure.

Migrations de longue durée et sans interruption

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

  1. Ajoutez d'abord des colonnes pouvant être nulles. L'ajout d'une colonne nullable 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 par petites quantités. Mettez en place un processus en arrière-plan pour remplir les nouvelles colonnes par morceaux plutôt que d’exécuter une seule grosse UPDATE.
  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éploiement par indicateurs de fonctionnalité. Déployez le code de l'application qui lit la nouvelle colonne derrière un indicateur de fonctionnalité. Une fois le remplissage en arrière terminé et les tests réussis, activez l'indicateur, puis supprimez ultérieurement l'ancien champ.

Rétrogradations et correctifs d'urgence

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

Insertion de données et modifications du schéma

  • Maintenez les seeders au minimum et stables. Utilisez les seeders pour les lignes essentielles de référence ou de configuration. Évitez de semer de gros 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 préproduction en premier. 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. Vérifiez et relisez les fichiers de migration dans les pull requests. Incluez des vérifications automatiques autant que possible.

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

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

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

Test des migrations

  • Testez les deux directions. Exécutez db:migrate puis db:migrate:undo:all localement et dans le CI pour vérifier que up et down fonctionnent tous les deux.
  • Utilisez des bases de données jetables pour les tests. Privilégiez les bases de données éphémères ou les conteneurs pour des tests déterministes.

Suivre ces meilleures pratiques garantit que vos migrations Sequelize restent fiables, traçables et sûres pour des projets à long terme.

Résolution des problèmes 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 blocages ou des conflits de concurrence. Cette section fournit des conseils pas à pas pour identifier et résoudre ces problèmes, ainsi que des bonnes pratiques de performance qui vous aident à maintenir votre application stable et efficace.

Dépannage des problèmes fréquents

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 les détails de votre connexion sont corrects. 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 vient probablement de votre service MySQL ou de vos identifiants.

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

Enfin, pour les connexions à distance, vérifiez les paramètres de votre pare-feu ou les règles de votre 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 se produisent 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 présents dans les noms de fichiers, il est donc important de vérifier que les fichiers sont correctement nommés.

Si une migration échoue, examinez attentivement le message d'erreur. 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, exécutez-le à nouveau.

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

3. Problèmes de synchronisation du modèle

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

Comment réparer :

Évitez d'utiliser sequelize.sync({ force: true }) en production, car cela supprimera et recréera les tables, entraînant une perte de données irréversible. De même, utilisez sequelize.sync({ alter: true }) avec prudence ; 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 permet d'identifier les incohérences avant qu'elles n'affectent la production.

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

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

Comment réparer :

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

Vous pouvez également ajuster la configuration du pool de Sequelize pour mieux adapter 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 latence élevée.

Comment diagnostiquer :

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

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

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

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

Si le résultat montre un « balayage complet de la table », la requête n'a pas d'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. Verrous morts 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 l'état d'InnoDB dans MySQL pour identifier quelles requêtes ou quels tableaux étaient impliqués :

SHOW ENGINE INNODB STATUS\G

Cette sortie comprend des informations détaillées sur les blocages récents et les attentes de verrouillage. Vous pouvez également activer les journaux généraux ou de transactions pour examiner la séquence des 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 réduire au minimum :

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

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

  3. Pour les blocages récurrents, mettez en place un mécanisme de nouvelle tentative simple avec un retour en arrière exponentiel :

    async function runWithRetry(fn, retries = 3) {
      for (let i = 0; i < retries; i++) {
        try {
          return await fn();
        } catch (err) {
          if (isDeadlockError(err) && i < retries - 1) {
            await sleep(100 * (i + 1));
            continue;
          }
          throw err;
        }
      }
    }
  4. Utilisez des verrouillages 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 les conflits.

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

1. Optimiser les requêtes

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

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

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

2. Utiliser efficacement les index

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. Ajuster le pool de connexions

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

  • Pour les petites applications, 5 à 10 connexions sont généralement suffisantes.
  • Pour les systèmes à forte charge, augmentez la taille du pool et ajustez les délais d'attente pour éviter les goulots d'étranglement des connexions. Surveillez régulièrement l'utilisation avec 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 à lecture intensive.

6. Paginer les résultats

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

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

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

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

FAQ

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

Sequelize est un ORM (mapper relationnel-objet) 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 brutes lorsque vous avez besoin de SQL direct.

2. Comment connecter Sequelize à MySQL ?

Pour connecter Sequelize à MySQL, vous créez une instance de Sequelize avec vos identifiants et options de base de données. 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 automatiquement les tables (idéal pour le développement). Voici un petit exemple :

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

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

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

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

4. Comment utiliser Sequelize avec Express ?

Dans une application Express, initialisez Sequelize dans un fichier séparé (comme db.js), exportez l'instance connectée et les modèles, et importez-les là où vous en avez besoin dans vos gestionnaires de routes ou vos contrôleurs. Utilisez async/await dans les gestionnaires de routes pour appeler les méthodes des modèles Sequelize, et gérez les erreurs avec try/catch ou un middleware 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 des requêtes arrivent.

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

Sequelize vous permet d'écrire la logique de base de données en JavaScript, tandis que SQL brut signifie écrire 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 résumé, utilisez Sequelize pour la plupart des tâches quotidiennes, et utilisez du SQL brut lorsque vous devez optimiser les performances ou exécuter quelque chose que Sequelize ne peut pas facilement exprimer.

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

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

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

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

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

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

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

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

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

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

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

npx sequelize-cli db:migrate

De cette façon, tout le monde dans votre équipe (et chaque déploiement) 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 votre application en production, car elles sont plus sûres, réversibles et plus faciles à suivre.

Conclusion

Dans ce tutoriel, vous configurez Sequelize avec Node.js et MySQL pour gérer les données 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 disposez désormais d'une base solide pour créer et maintenir des applications Node.js sécurisées et efficaces utilisant Sequelize et MySQL pour la gestion des données.

Pour en savoir plus sur Sequelize, consultez la documentation du produit. Pour d'autres tutoriels Node.js, 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.