Créer les dossiers suivants :
Le dossier models est organisé comme suit :
[nom_modele].js contenant chacun la définition d'un modèleindex.js chargé d'initialiser l'objet sequelize puis de charger les définitions des modèles et de réaliser les associationsLe code ci-après est celui du fichier index.js.
Les commentaires devraient vous suffire à comprendre son fonctionnement.
const
fs = require('fs'),
Sequelize = require('sequelize');
// create Sequelize instance
const sequelize = new Sequelize('[db_name]', '[username]', '[password]', {
host: '[hostname]',
port: 3306,
dialect: 'mysql',
dialectOptions: { decimalNumbers: true }
});
// this object will contain the model objects
// each key being the model's name
const db = {};
// read the files of the current directory
fs.readdirSync(__dirname)
.filter((filename) => filename !== 'index.js') // avoid this file
.forEach((filename) => {
const model = require('./' + filename)(sequelize); // get the model definition
db[model.name] = model; // add the entry in the db object
});
// go through each entry of the db object
Object.keys(db).forEach((modelName) => {
// call the "associate" function on the model object
// and pass it the db object (so that it can have access to other models)
db[modelName].associate(db);
});
// sync the DB
sequelize.sync();
// expose the db object
module.exports = db;
Dans ce code, on voit que l'on require chacun de nos fichiers dans lequel on définit un modèle.
Ce code s'attend à ce que le type obtenu soit une fonction, que l'on appelle immédiatement en lui passant l'instance sequelize, et qui nous retourne le modèle défini.
On observe également que l'on fait ensuite appel à la fonction associate sur chaque modèle.
Cette fonction doit faire partie des méthodes de classe du modèle, elle reçoit l'objet db en paramètre et c'est à cet endroit que l'on va déclarer d'éventuelles relations entre ce modèle et d'autres modèles définis dans db (voir https://sequelize.org/v5/manual/models-definition.html#expansion-of-models pour la documentation sur l'ajout de méthodes aux modèles).
Le code ci-dessous illustre un exemple de module définissant un modèle.
const Sequelize = require('sequelize');
module.exports = (sequelize) => {
class Bidule extends Sequelize.Model {
static associate(db) {
Bidule.hasMany(db.Machin);
};
}
Bidule.init({
bla: Sequelize.STRING,
bli: Sequelize.STRING
}, {
sequelize,
modelName: 'Bidule'
});
return Bidule;
};
Chaque fichier du dossier controllers définit un module qui exporte un objet contenant des fonctions qui seront utilisées en tant que middleware de nos routes express.
Comme on y utilise certainement les modèles, ces fichiers démarrent la plupart du temps par importer l'objet db contenant leur définition.
La fonction require générant un singleton, chacun des fichiers contrôleur peut récupérer l'objet db sans risquer d'exécuter plusieurs fois le code de définition des modèles.
Le code ci-dessous illustre un exemple de module définissant un contrôleur.
const db = require('../models');
module.exports = {
get_all: (req, res, next) => {
return db.Bidule.findAll()
.then((bidules) => res.json(bidules))
.catch((err) => next(err));
};
};
Le dossier routes est organisé comme suit :
[nom_routes].js contenant chacun des déclarations de routesindex.js qui charge les fichiers précédents et enregistre les routes auprès de l'application expressVoici du code pour le fichier index.js :
const
fs = require('fs');
module.exports = (app) => {
// read the files of the current directory
fs.readdirSync(__dirname)
.filter((filename) => filename !== 'index.js') // avoid this file
.forEach((filename) => {
// load routes array and register them
require('./' + filename).forEach((r) => {
app[r.method](r.url, r.func);
});
});
};
Dans ce code, on voit que chaque module chargé exporte un tableau d'objets (utilisation de la fonction forEach sur le résultat du require) contenant des informations sur les routes à enregistrer.
En particulier, on s'attend à ce que chaque objet du tableau contienne :
method indiquant la méthode HTTPurl indiquant l'url de la routefunc qui peut être un middleware ou un tableau de middlewaresLe code ci-dessous illustre un exemple de module définissant des routes :
const bidule_ctrl = require('../controllers/bidule');
module.exports = [
{
url: '/bidule',
method: 'get',
func: bidule_ctrl.get_all
}
];
Etant donné que les modules définissant les routes chargent les contrôleurs nécessaires, et que les modules définissant les contrôleurs chargent les modèles, l'application principale est réduite à la création de l'application express, le chargement du module routes et le lancement de l'écoute.