Skip to content

📅 SAISON B Semaine 5 Épisode 3 - Introduction aux ORM

📚 Notions du jour

Aujourd’hui, nous avons exploré un concept clé pour les applications modernes : les ORM (Object-Relational Mapping). Jusqu’à présent, nous utilisions un datamapper "manuel" pour interagir avec notre base de données. Cependant, cette approche présente des limites :

  • Code répétitif : Écrire manuellement chaque requête SQL devient fastidieux.
  • Risque d’erreurs : Les requêtes SQL mal écrites peuvent causer des bugs ou des failles de sécurité.
  • Couplage fort : Le code est fortement lié à la structure de la base de données.
  • Maintenance difficile : Les changements dans la base de données nécessitent des modifications dans tout le code.

Voici les notions aborbées durant cette journée :

  • Les limites d’un Data Mapper manuel dans un projet complexe.
  • Le concept d’ORM (Object-Relational Mapping) : un traducteur Objet <=> SQL.
  • Présentation de Sequelize comme solution ORM pour Node.js.
  • L’installation des paquets sequelize et pg.
  • La création et la configuration d’une instance de Sequelize.
  • Le test de la connexion à la base de données avec sequelize.authenticate().

Aujourd’hui, nous avons exploré un concept clé pour les applications modernes : les ORM (Object-Relational Mapping). Jusqu’à présent, nous utilisions un datamapper manuel pour interagir avec notre base de données. Cependant, cette approche présente des limites :

  • Code répétitif : Écrire manuellement chaque requête SQL devient fastidieux.
  • Risque d’erreurs : Les requêtes SQL mal écrites peuvent causer des bugs ou des failles de sécurité.
  • Couplage fort : Le code est fortement lié à la structure de la base de données.
  • Maintenance difficile : Les changements dans la base de données nécessitent des modifications dans tout le code.

🔧 Qu’est-ce qu’un ORM ?

Un ORM (Object-Relational Mapping) est une bibliothèque qui permet de :

  • Mapper les tables d’une base de données relationnelle (PostgreSQL, MySQL, etc.) à des objets JavaScript.
  • Automatiser la génération des requêtes SQL (CRUD : Create, Read, Update, Delete).
  • Abstraire les détails de la base de données, ce qui rend le code plus portable.

Un ORM comme Sequelize agit comme un traducteur entre le monde objet (JavaScript) et le monde relationnel (SQL). Il permet de :

Manipuler les données comme des objets (plus besoin d’écrire du SQL manuellement).
Générer automatiquement les requêtes SQL.
Gérer les relations entre tables (1:1, 1:N, N:N) de manière intuitive.
Sécuriser les requêtes (protection contre les injections SQL).

Exemple : Avec un ORM, au lieu d’écrire :

-- Requête SQL manuelle
INSERT INTO coffees (name, price) VALUES ('Espresso', 2.99);

On écrit :

// Avec un ORM (Sequelize)
await Coffee.create({ name: 'Espresso', price: 2.99 });

💠 Présentation de Sequelize

Sequelize est un ORM populaire pour Node.js qui supporte :

  • PostgreSQL, MySQL, SQLite, MSSQL, et MariaDB.
  • Les migrations pour gérer les changements de schéma.
  • Les relations entre modèles (1:1, 1:N, N:N).
  • Les transactions pour garantir l’intégrité des données.

📦 Installation et Configuration de Sequelize

1️⃣ Installer Sequelize et le Driver PostgreSQL

npm install sequelize pg pg-hstore
  • sequelize : Le cœur de l’ORM.
  • pg : Le driver pour se connecter à PostgreSQL.
  • pg-hstore : Pour gérer le type de données hstore de PostgreSQL (optionnel).

2️⃣ Configurer une Instance de Sequelize

Créer un fichier src/database/sequelize.js pour configurer la connexion à la base de données.

database/sequelize.js
import { Sequelize } from "sequelize";

// Crée l'instance de Sequelize
const sequelize = new Sequelize(process.env.DATABASE_URL, {
  // Define default parameters for Models with option `define: {...}`
  define: {
    // Option to keep the given table name as is
    freezeTableName: true,
    // Option to automatically manage createdAt/updatedAt columns
    timestamps: true,
    // Option that allows automatic generation of fields in snake_case (for exp: createdAt => created_at)
    underscored: true
  }
});

export default sequelize;

Explications :

  • Sequelize : Classe principale pour créer une instance de connexion.
  • process.env.DATABASE_URL : variable d'environnement contenant les paramètres de connexion à la BDD.
  • option define : permet de définir les paramètres par défaut que possèderont les Models de notre application.

3️⃣ Fichier .env pour les Variables d’Environnement

.env
DATABASE_URL=postgres://<user>:<password>@<host>:<port>/<database_name>

4️⃣ Tester la Connexion à la Base de Données

Avant de commencer à utiliser Sequelize, il est crucial de vérifier que la connexion à la base de données fonctionne.

database/sequelize.js
// ... (previous code)

  try {
    await sequelize.authenticate();
    console.log('Connection to the database OK');
  } catch(error) {
    console.error('Connection to the database NOT OK', error);
  }

// To test connexion to DB run: node --env-file=.env database/sequelize-client.js

Explications :

  • sequelize.authenticate() : Méthode pour tester la connexion.
  • Si la connexion réussit, un message de succès s’affiche. Sinon, une erreur est loguée.

📝 Créer un Modèle avec Sequelize

Un modèle (Model) dans Sequelize représente une table dans la base de données. Chaque modèle est une classe qui étend Model de Sequelize.

Définir un Modèle Coffee

// src/models/Coffee.js
import { DataTypes } from 'sequelize';
import sequelize from '../database/sequelize.js';

const Coffee = sequelize.define(
  'Coffee', // Model name
  {
    // Model attributes
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true;
    },
    description: {
      type: DataTypes.TEXT,
    },
    reference: {
      type: DataTypes.CHAR(9),
      allowNull: false,
      unique: true,
    },
    price_per_kg: {
      type: DataTypes.DECIMAL,
      allowNull: false,
    },
    available: {
      type: DataTypes.BOOLEAN,
      allowNull: false,
      defaultValue: false,
    },
  },
  {
    // Model options
    tableName: 'coffees', // Explicit table name
  }
);

export default Coffee;

Explications :

  • sequelize.define() : Définit un nouveau modèle.
    • Premier argument : Nom du modèle (Coffee).
    • Deuxième argument : Objet décrivant les colonnes de la table.
    • Troisième argument : Options du modèle (ex: tableName, timestamps).
  • DataTypes : Définit le type de chaque colonne (ex: STRING, INTEGER, DECIMAL).

💡 Ce Que J’ai Appris Aujourd’hui

  1. Un ORM comme Sequelize simplifie l’interaction avec une base de données en traduisant les objets JavaScript en requêtes SQL.
  2. La configuration de Sequelize est simple et permet de se connecter à différentes bases de données (PostgreSQL, MySQL, etc.).
  3. Les modèles Sequelize représentent des tables et permettent de définir des colonnes, des types de données, et des relations.

⌨️ Challenge du jour : "Projet oCoffee" – projet fil rouge (suite)


☕️ Présentation du Challenge

Aujourd’hui, l’objectif était d’intégrer Sequelize dans notre projet oCoffee pour remplacer notre datamapper manuel. Sequelize est un ORM (Object-Relational Mapping) qui permet de communiquer avec la base de données en JavaScript, sans écrire de SQL manuellement.

Pourquoi ?

  • Simplifier l’accès aux données : Plus besoin d’écrire des requêtes SQL à la main.
  • Gagner en sécurité : Sequelize protège contre les injections SQL et gère les connexions à la base de données.
  • Factoriser le code : Les modèles Sequelize permettent de représenter les tables de manière élégante et réutilisable.

Étapes à réaliser :

Reprendre les étapes vues en cours pour intégrer Sequelize à notre projet d'entraînement oCoffee.

  1. Installer les paquets nécessaires (sequelize et pg).
  2. Configurer une instance de Sequelize dans un fichier dédié (sequelize-client.js).
  3. Tester la connexion à la base de données.
  4. Définition du Model Country sur la base de ce que nous avons fait pour le Model Coffee.

📌 Étapes à Réaliser

Il ne sera pas nécessaire ici de présenter à nouveau les étapes réalisées pour l'intégration de Sequelize que j'ai déjà présentées ici 👉 intégration Sequelize


Définition du Model Country

Objectif : Définir un modèle Country avec Sequelize, en utilisant la syntaxe sequelize.define().

Ma proposition :

src/models/Country.js
import { DataTypes } from 'sequelize';
import sequelize from '../../database/sequelize-client.js';

const Country = sequelize.define(
  'country',
  {
    name: {
      type: DataTypes.STRING,
      allowNull: false,
      unique: true;
    },
  },
  {
    tableName: 'countries',
  }
);

export default Country;

💡 Ce Que J’ai Appris avec ce Challenge

  1. Sequelize simplifie la connexion à une base de données en gérant les détails techniques.
  2. La configuration de Sequelize est flexible et permet d’adapter son comportement (ex: freezeTableName, underscored).
  3. Tester la connexion avec sequelize.authenticate() est une étape cruciale pour s’assurer que tout fonctionne avant de continuer.
  4. Définir des modèles avec sequelize.define() permet de représenter les tables de manière claire.