Comment construire une API Rest avec NodeJS, Express et MySQL

Avec des connaissances en JavaScript et MySQL, nous pouvons construire notre API NodeJS en utilisant Express.

J’ai fait quelques recherches, et je tentais de développer une API à partir de zéro.
J’aime simplifier les choses et essayer d’éviter la duplication du code.

Ce guide vous montrera comment construire une API à partir de zéro :
Vous apprendrez comment créer des routes,
comment utiliser mysql2, comment configurer et se connecter à la base de données, et comment exécuter des requêtes avec des instructions préparées.
Comment créer un middleware qui peut obtenir un argument supplémentaire en plus de req, res, et le prochain callback.
Vous apprendrez à vérifier les données de l’objet de requête en utilisant le module Express Validator.
Vous apprendrez à utiliser le module JWT pour créer un jeton pour l’utilisateur, vérifier le jeton et obtenir l’objet stocké dans le jeton.
En outre, vous apprendrez à fournir aux utilisateurs la permission d’accéder à une certaine route en fonction de leurs rôles d’utilisateur.

Technologies et paquets :

  • NodeJS
  • Express
  • mysql2
  • bcryptjs
  • jsonwebtoken
  • express-validator
  • dotenv
  • cors

Installation de MySQL :

J’utilise WSL, et vous pouvez utiliser ce tutoriel pour voir comment installer MySQL dans WSL.
Vous devez vous assurer que MySQL fonctionne avec cette commande :

sudo service mysql status
Entrer dans le mode plein écran Quitter le mode plein écran

Si elle ne fonctionne pas, utilisez simplement :

sudo service mysql start
Entrer dans le mode plein écran Sortir du mode plein écran

Aperçu de l’application :

Nous allons construire une API de repos pour les opérations CRUD : créer, lire, mettre à jour et supprimer des utilisateurs.

Créer le dossier du projet et installer toutes les dépendances :

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Entrer dans le mode plein écran Sortir du mode plein écran

Aller au fichier package.json et changer la valeur “main” en “src/server.js” et ajouter ces scripts à l’objet scripts:

"start": "node src/server.js","dev": "nodemon"
Entrer dans le mode plein écran Sortir du mode plein écran

package.json devrait ressembler à ceci:

Créer le fichier .env :

Nous allons utiliser le fichier .env pour gérer toutes nos variables d’environnement.
Le fichier .env est un fichier caché qui nous permet de personnaliser nos variables d’environnement en utilisant la syntaxe ENV VARIABLE = VALUE.
Ces variables sont chargées à l’aide du module dotenv que nous avons déjà installé.
Le fichier .env peut être défini à différents stades de l’environnement (environnements de développement / stade / production).

Créer le fichier .env, copier les lignes suivantes, et mettre à jour le fichier avec votre db_name, db_username, et mot de passe MySQL:

# DB ConfigurationsHOST=localhostDB_USER=db_usernameDB_PASS=db_passwordDB_DATABASE=db_name# local runtime configsPORT=3000SECRET_JWT=supersecret
Entrer dans le mode plein écran Quitter le mode plein écran

Créer le fichier nodemon.json :

Nodemon est un outil qui aide à développer des applications basées sur node.js en redémarrant automatiquement l’application node lorsque des modifications de fichiers sont détectées dans le répertoire cible.
Le nodemon est un wrapper de remplacement pour node. Au lieu d’utiliser la commande node, nous devrions utiliser la commande nodemon sur la ligne de commande pour exécuter notre script.
Nous pouvons facilement ajouter des commutateurs de configuration tout en exécutant nodemon sur la ligne de commande, comme:

nodemon --watch src
Entrer dans le mode plein écran Quitter le mode plein écran

Nous pouvons également utiliser un fichier (nodemon.json) pour spécifier tous les commutateurs.
Si nous voulons surveiller plusieurs fichiers dans un répertoire, nous pouvons ajouter le répertoire dans le tableau “watch”.
Si nous voulons rechercher une extension particulière (comme un fichier ts), nous pouvons utiliser la propriété “ext”.
Si nous voulons ignorer certains fichiers, nous pouvons les définir dans le tableau “ignore”‘, et ainsi de suite…
J’utilise ce fichier surtout lorsque je crée un serveur avec NodeJS basé sur typescript, mais je pense qu’il est plus facile d’avoir plus d’endroits pour inclure les configurations de notre app.
Ce fichier est facultatif.

Créer le fichier nodemon.json et ajouter ceci au fichier:

{ "watch": , "ext": ".js", "ignore": }
Entrer dans le mode plein écran Sortir du mode plein écran

Créer le dossier src :

mkdir src && cd src
Entrer dans le mode plein écran Sortir du mode plein écran

Dans le dossier src, créer des sous-dossiers : contrôleurs, modèles, routes, middleware, db, et utils :

mkdir controllers models routes middleware db utils
Entrer dans le mode plein écran Sortir du mode plein écran

Configurer le serveur Express :

Dans le répertoire src, créez le fichier server.js et copiez ces lignes :

Dans ce fichier, nous importons express pour construire les API de repos et utilisons express.json() pour analyser les requêtes entrantes avec des charges utiles JSON.

Nous importons également le module dotenv pour lire le fichier de configuration .env config pour obtenir le numéro de port pour exécuter le serveur.

Nous importons également userRouter.

Après cela, nous avons un middleware qui gère les erreurs 404 → si quelqu’un cherche un endpoint qui n’existe pas, il obtiendra cette erreur : ‘Endpoint Not Found’ avec le code d’état 404. Après cela, nous utilisons le middleware error qui va récupérer les données d’erreur des routes précédentes. si next(err) est appelé, vous pouvez voir le middleware 404 à titre d’exemple.
Nous écoutons le port du fichier .env et l’imprimons sur la console que le serveur est en cours d’exécution.

Créer la base de données MySQL et la table utilisateur :

Dans le répertoire db, nous allons créer le fichier create-user-db.sql et copier-coller ces lignes :

Dans ce script, nous commençons par déposer la base de données si elle existe afin de pouvoir la réinitialiser rapidement en cas d’erreur (vous pouvez commenter cette ligne si vous le souhaitez), puis, nous créons la base de données si elle n’existe pas. Nous la définissons comme notre base de données active et créons une table “user” avec toutes les colonnes (id, username, etc.), ce qui permet une réinitialisation rapide si nécessaire. Vous pouvez exécuter cette requête dans votre client de base de données si vous en utilisez un.

Si vous utilisez wsl, dans le répertoire db, vous pouvez exécuter :

mysql -u -p < create-user-db.sql
Entrer en mode plein écran Quitter le mode plein écran

Configurer et se connecter à la base de données MySQL :

Créer un fichier supplémentaire dans le répertoire db appelé db-connection.js, et copier-coller ceci :

Dans ce fichier, nous importons d’abord le module dotenv et l’utilisons pour lire les infos de configuration de la base de données comme db host, db user à partir du fichier .env.

Nous vérifions la connexion au cas où il y aurait un problème avec la base de données, puis nous libérons la connexion.

Nous avons une méthode de requête qui retourne une promesse du résultat de la requête.

Nous utilisons un bloc try-catch pour capturer les erreurs MySQL courantes et retourner les codes d’état et les messages HTTP appropriés.

À la fin du fichier, nous créons une instance de la classe DBConnection et utilisons la méthode de requête, et dans le model.js (que nous verrons à l’étape suivante), nous utiliserons à nouveau la méthode de requête.

Créer un gestionnaire d’erreur :

Puis, nous allons créer notre gestionnaire d’erreur.

Pour ce faire, tout d’abord, nous allons créer le fichier HttpException.utils.js sous le répertoire utils, et copier-coller ce qui suit:

La classe HttpException hérite de la classe Error.
Le constructeur va récupérer le statut, le message, et les données. Nous passerons la variable message au constructeur parent en utilisant super(message), puis nous initialiserons les variables d’instance status, message, et data.

Après cela, nous créerons un gestionnaire d’erreur middleware dans le répertoire middleware.
Nous créerons un fichier error. middleware.js et copier-coller ce qui suit:

Nous pouvons voir en bas du fichier comment sera l’objet.

Le middleware obtiendra req, res, et le callback next, mais il obtiendra également un argument supplémentaire, error (en utilisant next(error) avant d’arriver à ce middleware).

Nous utilisons la déstructuration pour obtenir les variables de l’objet error et définir le statut à 500 s’il n’a pas été configuré auparavant.

Après cela, que le statut soit 500, nous nous assurerons de modifier le message afin que l’utilisateur reçoive un message d’erreur interne générique du serveur sans révéler la nature exacte de l’échec.

Après cela, nous créons un objet d’erreur avec les propriétés type, statut et message (les données sont facultatives).

Créer des fichiers utils (helpers) :

Dans le répertoire utils, nous créons deux autres fichiers, common.utils.js, et userRoles.utils.js.

common.utils.js:

Cette fonction aide à définir plusieurs champs pour les requêtes préparées avec des paires clé-valeur.
ColumnSet le tableau de paires clé = ?,
Les valeurs doivent donc être dans le même ordre que le tableau columnSet.

userRoles.utils.js:

module.exports = { Admin: 'Admin', SuperUser: 'SuperUser'}
Entrer dans le mode plein écran Sortir du mode plein écran

Créer une fonction asynchrone :

Créer un autre fichier appelé awaitHandlerFactory.middleware.js dans le répertoire middleware et copier-coller ceci:

En général, nous savons que le middleware est seulement une méthode asynchrone qui obtient les arguments req, res, et next, donc, si nous voulons que ce middleware obtienne un argument supplémentaire, nous le ferons de cette façon (nous utiliserons ceci dans le middleware auth également dans la prochaine étape).

Cette fonction obtiendra un callback, exécutera le script du middleware, et tentera de déclencher ce callback dans le bloc try.
Si quelque chose ne va pas ici, elle attrapera l’erreur et nous utiliserons le next(err) (qui le transférera au prochain middleware => error.middleware.js).

Créer le middleware d’authentification :

Un autre middleware dont nous avons besoin est le middleware d’authentification que nous utiliserons pour vérifier les permissions des utilisateurs via le module JWT.

Similaire au middleware awaitHandlerFactory.middleware.js, nous avons ici un middleware qui nécessite un argument supplémentaire (qui est facultatif) => rôles.

J’ai utilisé try-catch pour ajuster le statut d’erreur dans la zone de capture à 401 (si le jeton a expiré, par exemple).

Dans un premier temps, nous recherchons req.headers.authorization – s’il n’est pas défini dans l’en-tête ou si l’en-tête ne commence pas par “Bearer “, l’utilisateur recevra une réponse 401. S’il commence par “Bearer “, nous obtiendrons le jeton et utiliserons la clé secrète du fichier .env pour le déchiffrer.

Nous vérifierons le jeton en utilisant la fonction synchrone jwt.verify, qui obtient le jeton, et la secretKey, comme arguments et renvoie la charge utile décodée, si la signature est valide et les champs optionnels expiration, audience ou issuer sont valides. Sinon, elle lancera une erreur.

Maintenant, nous pouvons trouver l’utilisateur avec ce jeton en recherchant l’id utilisateur.
Si l’utilisateur n’existe plus, ils obtiendront une exception de 401 sans aucune information.
Si l’utilisateur existe, nous allons vérifier si l’utilisateur actuel est le propriétaire qui recherche ses routes ou si l’utilisateur a le rôle pour accéder à cette route.
Nous sauvegardons l’utilisateur actuel juste au cas où il veut obtenir ses données sur le prochain middleware (comme la route “whoami”).

Validation des données en utilisant le module Express Validator :

Dans le répertoire middleware, nous allons créer un fichier supplémentaire que nous utiliserons pour vérifier les propriétés req.body.

Créer un sous-dossier dans le répertoire middleware appelé validators et créer un fichier dans ce répertoire, userValidator.middleware.js. Copiez-collez ceci:

Dans ce fichier, j’ai utilisé le module express-validator, qui est très facile à utiliser chaque fois que nous devons vérifier certaines propriétés, vérifier si la propriété existe, ou créer des vérifications personnalisées avec un message personnalisé à l’utilisateur si une valeur de propriété n’est pas valide.

Maintenant, nous pouvons commencer à créer nos fichiers de route, de contrôleur et de modèle.

Définir les routes :

Créer le fichier user.route.js dans le répertoire routes et copier-coller ceci:

L’exemple ci-dessus montre comment définir les routes. Essayons de le décomposer en morceaux:

  • Vous pouvez créer un routeur en utilisant express.Router().Chaque route peut charger une fonction middleware qui gère la logique métier.UserController, par exemple porte tous les middlewares principaux.Pour utiliser le routeur, celui-ci doit être exporté en tant que module et utilisé dans l’application principale en utilisant app.use(router_module).
  • Nous avons utilisé le middleware auth pour l’authentification et l’autorisation de l’utilisateur, pour vérifier le jeton de l’utilisateur ou le rôle de l’utilisateur pour la route.Dans notre exemple, certaines des routes utilisent le middleware auth pour vérifier l’authentification et l’autorisation de l’utilisateur.Ce middleware sera déclenché avant le middleware principal (celui qui contient la logique métier).Le prochain callback doit être appelé pour passer le contrôle à la prochaine méthode du middleware.Sinon, la requête sera laissée en suspens.
  • awaitHandlerFactory (middleware try-catch) est utilisé pour envelopper tous les middleware asynchrones. De cette façon, si l’un des intergiciels jette une erreur, awaitHandlerFactory attrapera cette erreur.Vous pouvez voir que toutes nos fonctions d’intergiciel sont enveloppées avec l’intergiciel awaitHandlerFactory, ce qui nous aide à gérer nos erreurs en utilisant try-catch en un seul endroit.
  • En outre, nous avons le schéma createUserSchema, updateUserSchema et validateLogin pour valider le corps avant de commencer le prochain middleware.

La syntaxe de la méthode HTTP est:

Créer le contrôleur :

Créer le fichier user.controller.js dans le répertoire controllers et copier-coller ceci:

Comme mentionné ci-dessus, le fichier controller contient notre logique métier pour la gestion de nos routes.
Dans notre exemple, certaines méthodes utilisent la classe UserModel pour interroger la base de données afin d’obtenir les données.
Pour retourner les données dans chaque middleware, nous utilisons res.send(result) pour envoyer une réponse au client.

Créer le modèle :

Et créer le fichier user.model.js dans le répertoire models et copier-coller ceci :

Cette classe établit la connexion entre le contrôleur et la base de données.
Ici nous avons toutes les méthodes qui obtiennent les arguments du contrôleur, font une requête, préparent les déclarations, se connectent à la base de données en utilisant la méthode de requête de la classe db-connection, envoient la requête avec le tableau des déclarations préparées et récupèrent le résultat.
Chaque fonction renvoie le résultat au contrôleur.

.gitIgnore :

Au cas où vous décideriez d’ajouter ce projet à votre GitHub, n’oubliez pas de créer un fichier .gitignore et de copier-coller ceci :

node_modules.env
Entrer dans le mode plein écran Quitter le mode plein écran

Ce fichier indique simplement à git quels fichiers il doit ignorer.
Vous devriez éviter le répertoire node_modules car il est lourd et non nécessaire pour le dépôt.
Lorsque quelqu’un clone ce dépôt, il utilisera la commande “npm I” pour installer toutes les dépendances.
Ignorer le fichier .env consiste à cacher vos configurations privées aux autres développeurs utilisant votre code.

.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.