Express 01.2 - đ Lecture depuis la base de donnĂ©es
â ïž Avant de commencer cette quĂȘte, nous te conseillons de terminer celles-ci :
- QuĂȘte 2117 (hors archive)
- QuĂȘte 1024
- QuĂȘte 484
Dans les quĂȘtes prĂ©cĂ©dentes, tu as vu comment configurer et crĂ©er un serveur avec Express et comment crĂ©er des routes de base. Lors du dernier challenge, les donnĂ©es que tu utilisais provenaient d'un tableau JavaScript et non d'une base de donnĂ©es.
Dans cette quĂȘte, il est temps d'apprendre Ă connecter ton serveur Express Ă ta base de donnĂ©es SQL.

đ€ Dans cette quĂȘte, tu vas apprendre Ă :
â Connecter Express Ă une base de donnĂ©es SQL â ProtĂ©ger des donnĂ©es sensibles avec dotenv â Tester une route avec Postman
âïž Mise en place de l'application Express
Commence par forker ce dĂ©pĂŽt sur ton compte GitHub. Clone ensuite ton fork sur ta machine. Jette un Ćil au code : c'est la mĂȘme application que celle que tu as créée dans la quĂȘte prĂ©cĂ©dente, sauf que les gestionnaires de films ont Ă©tĂ© dĂ©placĂ©s dans un fichier sĂ©parĂ© src/controllers/movieControllers.js. De cette maniĂšre, notre src/app.js se charge uniquement de dĂ©clarer les routes :
const express = require("express");
const app = express();
const movieControllers = require("./controllers/movieControllers");
app.get("/api/movies", movieControllers.getMovies);
app.get("/api/movies/:id", movieControllers.getMovieById);
module.exports = app;
Nous allons ajouter plus de routes dans notre application : il est important d'organiser le code avant qu'il ne devienne trop gros.
Comme tu t'en souviens, nous utilisons le port 5000 dans notre application comme port par dĂ©faut sur lequel le serveur Ă©coutera. Mais s'il n'est pas disponible, tu peux le changer pour autre chose ! Cela peut ĂȘtre utile pour Ă©viter les conflits de port avec une autre application en cours d'exĂ©cution sur ta machine qui utilise dĂ©jĂ le port 5000.
Note que c'est une bonne pratique de gĂ©rer cela via un fichier d'environnement : un fichier .env contenant ta configuration spĂ©cifique. Ce fichier .env peut alors ĂȘtre chargĂ© en utilisant le module dotenv.
Dotenv est un module sans dépendance qui charge les variables d'environnement à partir d'un fichier .env dans process.env. Cela nous permet de stocker des informations sensibles (noms d'utilisateur, mots de passe, tokens...) dans un fichier .env qui ne sera pas envoyé sur GitHub. Par convention, les variables d'environnement sont en MAJUSCULES, pour faire la différence avec les variables classiques.
Si tu modifies le port dans ton fichier .env et que tu lui donnes la valeur 5001 par exemple, garde simplement Ă l'esprit que tu devras te rendre sur localhost:5001 au lieu de localhost:5000 lorsque tu vois un lien "localhost" dans les quĂȘtes.
Commence par installer dotenv dans ton projet :
npm install dotenv
Tu vas maintenant crĂ©er ton fichier .env. Tu ne devras pas le partager sur GitHub, car il contiendra bientĂŽt des informations sensibles (mot de passe de la base de donnĂ©es par exemple). Mais si tu ne le partages pas, comment les "cloneurs" de ton projet sauront-ils ce qui doit ĂȘtre dĂ©clarĂ© dans le fichier ? Une autre bonne pratique consiste Ă partager un exemple de fichier au lieu du vĂ©ritable .env. Le fichier d'exemple permettra aux cloneurs de savoir quelles variables doivent ĂȘtre dĂ©clarĂ©es pour ton application. C'est pourquoi tu peux voir un fichier .env.sample Ă la racine du projet. Copie-le :
cp .env.sample .env
Et ajoute ce .env Ă ton .gitignore. Regarde maintenant ton fichier index.js. Pour utiliser le module dotenv, tu dois ajouter cette ligne en haut de ton fichier :
require("dotenv").config();
Cela chargera ton fichier .env et remplira process.env avec toutes tes variables.
Maintenant, tu peux consommer la variable APP_PORT comme suit:
const port = process.env.APP_PORT;
Maintenant que ton environnement est configuré, tu peux exécuter npm run dev, aller sur localhost:5000 et vérifier que ton serveur est en cours d'exécution.
đïž CrĂ©er la base de donnĂ©es
Nous avons déjà préparé pour toi une petite base de données contenant des films et des utilisateurs. Tu peux prendre le temps de regarder le fichier express_quests.sql à la racine du projet.
Ouvre un terminal dans le rĂ©pertoire oĂč se trouve le fichier, ouvre un client MySQL dans ton terminal (avec la commande mysql). CrĂ©e une nouvelle base de donnĂ©es appelĂ©e express_quests avec CREATE DATABASE puis sĂ©lectionne cette base de donnĂ©es avec USE :
CREATE DATABASE express_quests;
USE express_quests;
Et source le fichier pour créer les tables:
source express_quests.sql
Si cela ne fonctionne pas, essaye de copier coller tout le contenu du fichier dans le terminal MySQL et appuie sur Entrée.
ExĂ©cute maintenant cette requĂȘte :
SELECT * FROM movies;
Tu devrais voir ceci dans ton terminal :

Maintenant, tu peux quitter mysql en utilisant exit.
đŠ Installer le module MySQL 2
Pour communiquer avec ta base de données, tu dois installer un module appelé mysql2 :
npm install mysql2
Tu peux trouver la documentation de ce package ici :
Un client JavaScript pur Node.js implémentant le protocole MySQL.
âïž Configurer l'accĂšs Ă la base de donnĂ©es
Pour configurer le package afin d'accéder à la base de données, tu dois ajouter des variables à ton fichier .env, avec toutes les informations nécessaires pour te connecter à la base de données :
DB_HOST=localhost
DB_PORT=3306
DB_USER=REPLACE_WITH_YOUR_USERNAME
DB_PASSWORD=REPLACE_WITH_YOUR_PASSWORD
DB_NAME=REPLACE_BY_DB_NAME
Sur certains OS, localhost devra ĂȘtre remplacĂ© par 127.0.0.1 (en savoir plus).
N'oublie pas de remplacer les valeurs par celles qui conviennent à ta base de données.
Le port MySQL par dĂ©faut est 3306, mais selon la façon dont tu l'as installĂ© et configurĂ©, cela pourrait ĂȘtre autre chose ! (3309 avec une installation Docker)
Note que tu dois également mettre à jour ton fichier .env.sample avec les lignes ci-dessus. Conserve les fausses valeurs REPLACE_WITH_... pour celui-ci.
Ton fichier .env.sample doit toujours refléter le vrai fichier .env, mais toujours avec des exemples de valeurs et non avec de vraies valeurs.
Maintenant, jouons avec le module mysql2.
Crée un nouveau fichier nommé database.js à cÎté de ton index.js.
Tout en haut, nous devons importer le package dotenv et exécuter config(). Cela définira pour nous toutes les variables d'environnement que nous avons décrites dans le fichier .env.
require("dotenv").config();
Ensuite, importe le package mysql2Â :
const mysql = require("mysql2/promise");
Enfin, utilise mysql.createPool pour préparer un pool de connexion à l'aide des variables d'environnement que tu viens de créer :
require("dotenv").config();
const mysql = require("mysql2/promise");
const database = mysql.createPool({
host: process.env.DB_HOST, // address of the server
port: process.env.DB_PORT, // port of the DB server (mysql), not to be confused with the APP_PORT !
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
Tu peux essayer d'obtenir une premiÚre connexion depuis le pool pour vérifier que tout va bien :
database
.getConnection()
.then(() => {
console.log("Can reach database");
})
.catch((err) => {
console.error(err);
});
Et lancez la commande suivante :
npx nodemon database.js
đŹ Ăcrire une requĂȘte
En utilisant notre objet database, nous pouvons envoyer des requĂȘtes Ă notre serveur MySQL en utilisant la mĂ©thode query().
La mĂ©thode a besoin d'une chaĂźne de caractĂšres comme premier paramĂštre : le code SQL de notre requĂȘte.
Puisque nous utilisons la version avec des promesses, nous devrons chaĂźner l'appel Ă query() avec un .then() (et un .catch() pour intercepter les erreurs).
Dans database.js essaye les lignes suivantes :
database
.query("select * from movies")
.then((result) => {
console.log(result);
})
.catch((err) => {
console.error(err);
});
đđ» Il n'est pas obligatoire d'utiliser la mĂ©thode
getConnection(): faire une requĂȘte crĂ©era aussi automatiquement une connexion. Cependant, il est recommandĂ© de l'utiliser pour dĂ©boguer ton serveur en cas d'Ă©chec de la connexion.
Lors du rechargement du script, tu devrais voir beaucoup de choses dans ton terminal.
En prĂȘtant attention, tu verras que result est un tableau contenant les lignes sĂ©lectionnĂ©es comme premier Ă©lĂ©ment.
Le reste du tableau contient des informations supplĂ©mentaires concernant la requĂȘte. Nous allons nous concentrer sur les lignes sĂ©lectionnĂ©es et les extraire du tableau result :
database
.query("select * from movies")
.then((result) => {
const movies = result[0];
console.log(movies);
})
.catch((err) => {
console.error(err);
});
Ou, en utilisant la déstructuration de tableau :
database
.query("select * from movies")
.then((result) => {
const [movies] = result;
console.log(movies);
})
.catch((err) => {
console.error(err);
});
Ou directement en destructurant le paramĂštre :
database
.query("select * from movies")
.then(([movies]) => {
console.log(movies);
})
.catch((err) => {
console.error(err);
});
Si tout s'est bien passé, tu devrais voir les films s'afficher dans ta console.
Maintenant, déplaçons ce morceau de code dans la fonction getMovies que nous avons défini dans movieControllers.js (n'oublie pas d'ajouter module.exports = database; à la fin de database.js ) :
const movies = [/* ... */];
const database = require("../../database");
const getMovies = (req, res) => {
database
.query("select * from movies")
.then(([movies]) => {
res.json(movies); // use res.json instead of console.log
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
Pour compléter le console.error lorsque des erreurs sont trouvées, nous envoyons un statut 500 au client. Sinon, nous pouvons renvoyer un statut 200 et les données.
Maintenant, arrĂȘte le script database (Ctrl+C) et exĂ©cute npm run dev. Si tu vas sur localhost:5000/api/movies, tu devrais voir la liste des films de ta base de donnĂ©es.

đŹ Ăcrire une requĂȘte avec un paramĂštre
Maintenant, ta fonction getMovieById devrait Ă©galement ĂȘtre mise Ă jour pour trouver un film dans la base de donnĂ©es. Tu voudras peut-ĂȘtre faire quelque chose comme ceci :
const getMovieById = (req, res) => {
const id = parseInt(req.params.id);
database
.query(`select * from movies where id = ${id}`)
.then(...)
.catch(...);
}
Cette façon d'injecter l'id dans une requĂȘte SQL n'est pas sĂ»re : tu devrais utiliser des requĂȘtes prĂ©parĂ©es Ă la place. Cela signifie que tu devras remplacer chaque variable dans ta chaĂźne SQL par un ?. Ensuite, les valeurs Ă injecter seront passĂ©es dans un tableau en tant que second paramĂštre de query(). Quelque chose comme ca :
const getMovieById = (req, res) => {
const id = parseInt(req.params.id);
database
.query("select * from movies where id = ?", [id])
.then(...)
.catch(...);
};
Essaye de complĂ©ter le then et le catch toi-mĂȘme, sachant que tu dois renvoyer un seul objet film si le film recherchĂ© existe (pas de tableau). Sinon, tu dois renvoyer un statut 404 si le film recherchĂ© n'existe pas. Renvoie un statut 500 si tu as dĂ©tectĂ© une erreur.
Afficher la solution|||javascript|||Tu as des doutes sur comment faire ? |||0|||Masquer
const getMovieById = (req, res) => {
const id = parseInt(req.params.id);
database
.query("select * from movies where id = ?", [id])
.then(([movies]) => {
if (movies[0] != null) {
res.json(movies[0]);
} else {
res.sendStatus(404);
}
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
đŹ Postman

Jusqu'Ă prĂ©sent, tu utilisais ton navigateur Web pour vĂ©rifier les rĂ©ponses du serveur. Dans les prochaines quĂȘtes, nous allons Ă©galement faire des requĂȘtes POST pour ajouter des objets dans notre base de donnĂ©es.
Parce que tu n'as pas encore de frontend, il va ĂȘtre difficile de faire ces requĂȘtes POST depuis ton navigateur. Pour gĂ©rer cela, tu peux installer un logiciel appelĂ© Postman.
Postman est un excellent outil que tu peux utiliser pour tester tes routes.
Voici le lien pour le télécharger :
Une fois tĂ©lĂ©chargĂ© et installĂ©, ouvre le logiciel et crĂ©e une nouvelle requĂȘte HTTP (bouton "+" aprĂšs l'onglet "Overview").
Dans la section "url", tapez l'url de ton serveur, localhost:5000/api/movies et assure toi que GET est sélectionné puis clique sur envoyer.

Tu devrais voir la réponse de ton serveur :

Tutorial: How to create a query with Postman
đȘ Challenge
Création de routes utilisateur
Ton défi consiste à :
- Créer une route GET
/api/users, cette route doit renvoyer un statut200et une liste d'utilisateurs de la base de données au format json - Créez une route GET
/api/users/:idqui renverra uniquement l'utilisateur de la base de données correspondant à l'identifiant défini dans l'url- S'il y a un utilisateur qui correspond aux paramÚtres, renvoie une réponse avec un statut
200et l'utilisateur correspondant en tant qu'objet json - Sinon, retourne un statut
404
- S'il y a un utilisateur qui correspond aux paramÚtres, renvoie une réponse avec un statut
Publie une URL d'un dépÎt GitHub avec ton application complÚte comme solution.
Des tests sont fournis pour ces 2 routes dans tests/users.test.js. Tu peux utiliser la commande npm run test pour vérifier ton code.
Lorsque tu lances la commande npm run test celle-ci ne se termine jamais ! Tu dois résoudre ce point avec les informations du paragraphe suivant.
Gestion du cycle de vie des tests
Jest met à notre disposition un grand nombre de fonctionnalités. Celle qui va nous intéresser ici va nous permettre d'exécuter une action aprÚs l'exécution de tous nos tests : cette fonction s'appelle afterAll (tu peux voir dans la documentation qu'il en existe d'autres pour exécuter une action avant ou aprÚs chaque test par exemple).
Dans notre cas ce qui empĂȘche notre script de test de terminer son exĂ©cution, c'est la connexion Ă la base de donnĂ©es, il va donc falloir la couper aprĂšs l'exĂ©cution de tous les tests.
Pour ce faire, dans chaque fichier de test importe la connexion à la base de données et utilise afterAll pour la couper "manuellement" :
const request = require("supertest");
const app = require("../app");
const database = require("../database")
afterAll(() => database.end());
// ...
En ajoutant ces deux lignes à ton fichier de test ceux-ci devraient terminer leur exécution normalement !
đ§ CritĂšres de validation
- [ ] Le serveur fonctionne
- [ ] Les tests passent avec
npm run test - [ ] La commande
npm run testse termine bien
==$==
Ce que tu devrais avoir dans :
require("dotenv").config();
const app = require("./src/app");
const port = process.env.APP_PORT ?? 5000;
app
.listen(port, () => {
console.log(`Server is listening on ${port}`);
})
.on("error", (err) => {
console.error("Error:", err.message);
});
require("dotenv").config();
const mysql = require("mysql2/promise");
const database = mysql.createPool({
host: process.env.DB_HOST, // address of the server
port: process.env.DB_PORT, // port of the DB server (mysql), not to be confused with the APP_PORT !
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
database
.getConnection()
.then(() => {
console.log("Can reach database");
})
.catch((err) => {
console.error(err);
});
module.exports = database;
const express = require("express");
const app = express();
const movieControllers = require("./controllers/movieControllers");
app.get("/api/movies", movieControllers.getMovies);
app.get("/api/movies/:id", movieControllers.getMovieById);
const userControllers = require("./controllers/userControllers");
app.get("/api/users", userControllers.getUsers);
app.get("/api/users/:id", userControllers.getUserById);
module.exports = app;
const database = require("../../database");
const getUsers = (req, res) => {
database
.query("select * from users")
.then(([users]) => {
res.json(users);
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
const getUserById = (req, res) => {
const id = parseInt(req.params.id);
database
.query("select * from users where id = ?", [id])
.then(([users]) => {
if (users[0] != null) {
res.json(users[0]);
} else {
res.sendStatus(404);
}
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
module.exports = {
getUsers,
getUserById,
};
const database = require("../../database");
const getMovies = (req, res) => {
database
.query("select * from movies")
.then(([movies]) => {
res.json(movies);
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
const getMovieById = (req, res) => {
const id = parseInt(req.params.id);
database
.query("select * from movies where id = ?", [id])
.then(([movies]) => {
if (movies[0] != null) {
res.json(movies[0]);
} else {
res.sendStatus(404);
}
})
.catch((err) => {
console.error(err);
res.sendStatus(500);
});
};
module.exports = {
getMovies,
getMovieById,
};
Ressources partagĂ©es sur cette quĂȘte
- J'ai eu une erreur liée à l'authentification au server MySQL via le module importé, la 1ere réponse m'a permis de corriger cette erreurhttps://stackoverflow.com/questions/50093144/mysql-8-0-client-does-not-support-authentication-protocol-requested-by-server
- Faire les quĂȘtes MySQL avant !https://odyssey.wildcodeschool.com/quests/1597
- La connexion ne se faisait pas avec mysql (ECONNREFUSED), il fallait ajouter une socketPath pour établir la connexion !https://stackoverflow.com/questions/30266221/node-js-mysql-error-connect-econnrefused
- Etape a suivre une fois qu'on a téléchargé postman pour ubuntu 20.04https://speedysense.com/install-postman-on-ubuntu-20-04/
- Pour savoir le port de votre mysql https://www.it-swarm-fr.com/fr/mysql/comment-tester-le-port-utilise-par-mysql-et-savoir-sil-peut-etre-connecte/973140410/