Utilisation de Node.js, Express et Express Router pour implémenter une API REST très simple

Aug 23 2020

Pour apprendre les ficelles de Node.js et Express, j'essaie d'implémenter une API REST très simple. L'API dit ce qu'elle doit faire au lieu de le faire, donc je peux me concentrer sur la structure du framework. La configuration requise pour l'API est la suivante :

  • Mock API pour un jeu de questions et réponses.
  • /qanda endpoint : référentiel de toutes les paires question-réponse.
  • Point de terminaison /qanda/questionId : une paire question-réponse spécifique.
  • Doit gérer les verbes HTTP GET, POST, PUT, DELETE.
  • Supposons que le corps de la requête HTTP est JSON.

Tout cela est arbitraire. J'essaie juste de maîtriser le traitement et l'analyse des requêtes et des réponses HTTP. Pour garder les choses organisées, j'utilise également les modules Express Router et Node.js.

Voici index.js

'use strict';
const express = require('express');

const qanda = require('./routes/qanda.js');

const port = 9999;
const app = express();

app.use('/qanda', qanda);

app
  .all('/', (req, res, nxt) => {
  res
    .status(200)
    .send('Welcome!');
  })
  .listen(port, () => {
    console.log(`Listening to localhost:${port}`);
  });

Et voici ./routes/qanda.js

'use strict';
const express = require('express');

const qandaRouter = express.Router();
qandaRouter.use(express.json());

qandaRouter
  .route('/')
  .get((req, res, nxt) => {
    res
      .status(200)
      .send('Sending all the qandas!');
  })
  .post((req, res, nxt) => {
    res
      .status(200)
      .send(`Adding ${req.body.q} = ${req.body.a}`); }) .put((req, res, nxt) => { res .status(405) .send('PUT not supported.'); }) .delete((req, res, nxt) => { res .status(200) .send('Deleting all qands!'); }); qandaRouter .route('/:qId') .get((req, res, nxt) => { res .status(200) .send(`Sending qanda ${req.params.qId}`);
  })
  .post((req, res, nxt) => {
    res
      .status(405)
      .send('POST not supported.');
  })
  .put((req, res, nxt) => {
    res
      .status(200)
      .send(`Updating ${req.params.qId} with ${req.body.q} = ${req.body.a}`); }) .delete((req, res, nxt) => { res .status(200) .send(`Deleting qanda ${req.params.qId}`);
  });

module.exports = qandaRouter;

J'attends avec impatience toutes sortes de commentaires! Du style aux meilleures pratiques, en passant par les anti-modèles, tout ce à quoi vous pouvez penser est très apprécié ! J'ai quelques questions précises :

  • Le chaînage de méthodes est-il la manière habituelle de gérer quelque chose comme ça dans Express ?
  • Le style de chaînage des méthodes est-il lisible et maintenable ?
  • Existe-t-il un moyen plus concis d'y parvenir?
  • Que ferais-tu différemment?

Réponses

6 KaterAkeren Aug 23 2020 at 20:44

Après avoir examiné les codes que vous avez écrits, j'ai pu voir certaines choses qui ne sont pas bien faites. Voici les quelques éléments que j'ai soulignés :

  • La structuration de la base de code n'est pas correcte, elle peut donc être maintenable et évolutive si la base de code se développe.
  • La convention de dénomination de l'API RESTful n'est pas acceptable. Les points de terminaison de toute API REST ne doivent contenir que des ressources (noms) et utiliser des méthodes HTTP (verbes) pour les actions. Vous avez nommé votre point de terminaison comme /qanda au lieu de /qandas . Je vous recommande de lire ces directives de conception d'API RESTful par Mahesh Haldar publiées sur Hackernoon. C'est une excellente ressource pour les développeurs.
  • Troisièmement, vous n'avez pas séparé votre serveur d'express ; ils sont tous les deux couplés. Ce n'est pas la meilleure pratique de le faire.
  • Lorsque l'API est consommée par d'autres applications, la mise à niveau des API avec certaines modifications entraînerait également la rupture du contrat de services existant - cela a été remarqué par de nombreuses applications qui fournissent des services API. Il est recommandé de toujours préfixer toutes vos URL avec /api/v1/users . En cas de mise à jour majeure, nous pouvons nommer le nouvel ensemble d'API v2, v3 ou v1.XX . respectivement.

D'après mon expérience, voici comment je l'aborderais:

  • Tout d'abord, je vais créer un répertoire appelé qanda-api , à l'intérieur, je créerai d'autres répertoires appelés src et deux fichiers app.js et server.js qui résident dans le répertoire racine avec src
  • Deuxièmement, à l'intérieur de src , je vais créer deux sous-répertoires appelés routes et controllers
  • Troisièmement, je vais initialiser un registre npm à la racine du répertoire du projet qui garde une trace de tous les packages npm installés en tapant la commande ci-dessous via le terminal :

initialisation npm

  • Après cela, j'installerai les trois dépendances que j'utiliserai via le même terminal, mais cette fois vous devez avoir un accès à Internet :

npm installer express dotenv morgan

  • Si le processus ci-dessus s'exécute avec succès, vous verrez les packages installés dans un fichier appelé package.json

Maintenant, faisons le codage en nous salissant les mains

  • Commençons par créer un fichier nommé controllers/qandaController.js dans other pour écrire notre logique qanda qui sera exportée vers son fichier de route.
exports.getAllQandas = (req, res) => {
    res.status(200).json({
        status: 'success',
        message: 'Documments retrieved successfully',
        data: 'The data goes here from the data!'
    });
};

exports.getQanda = (req, res) => {
    res.status(200).json({
        status: 'success',
        message: 'Doc retrieved successfully',
        data: 'The data goes here from the data!'
    });
};

exports.createQanda = (req, res) => {
    res.status(201).json({
        status: 'success',
        message: 'Qanda created successfully',
        data: 'Return the created data here!'
    });
};

exports.updateQanda = (req, res) => {
    res.status(200).json({
        status: 'success',
        message: 'Doc updated successfully',
        data: 'The updated data goes here!'
    });
};

exports.deleteQanda = (req, res) => {
    res.status(200).json({
        status: 'success',
        message: 'Doc deleted!',
        data: 'Return the Deleted data'
    });
};
  • Maintenant, il est temps pour nous de créer notre route qanda dans routes/qandaRoutes.js . Nous importerons ensuite la logique du contrôleur dans son gestionnaire de routage ci-dessous.
const express = require('express');
/**
 * import the qanda controller
 */
const qandaController = require('./../controllers/qandaController');

const router = express.Router();

router
    .route('/')
    .get(qandaController.getAllQandas)
    .post(qandaController.createQanda);

router
    .route('/:id')
    .get(qandaController.getQanda)
    .patch(qandaController.updateQanda)
    .delete(qandaController.deleteQanda);

module.exports = router;
  • Ouvrons le fichier que nous avons créé précédemment appelé app.js dans other pour utiliser l'express que nous avions installé. Le contenu de ce fichier est uniquement pour express qui est ensuite exporté vers son serveur :
const express = require('express');
const morgan = require('morgan');

const qandaRouter = require('./routes/qandaRoutes');

const app = express();
    
if (process.env.NODE_ENV === 'development') app.use(morgan('dev'));

// ROUTES
app.use('/api/v1/qandas', qandaRouter);
    
/*
 ** HANDLING UNHANDLED ROUTES
 */
app.all('*', (req, res, next) => {
    res.status(404).json({
        status: 'fail',
            message: `Can't find ${req.originalUrl} on this Server!`
    });
});
   
module.exports = app;
  • Créons notre serveur qui devient notre point d'entrée dans l'application. C'est là que l'exécution réelle se produit, où l' app.js exporté est utilisé en l'insérant comme argument dans la fonction createServer().
require('dotenv').config({ path: '.env' });
const http = require('http');

const app = require('./app');

const port = process.env.PORT || 5000;

const server = http.createServer(app);

server.listen(port, () => console.log(`App running on port ${port}`));

Conclusion

Il est fortement recommandé de toujours séparer les préoccupations lors de l'écriture de codes et d'avoir une base de code propre, maintenable et évolutive dans le temps. Il y a plus dans l'organisation du code en matière de conception d'API, l'un d'entre eux est le principe DRY (Don't Repeat Yourself). Si vous voulez en savoir plus sur la conception d'API dans Node.JS, je vous recommande de suivre ce cours par Jonas . Il démystifie les concepts abstraits et enseigne les meilleures pratiques à la base. J'ai enveloppé cette réponse avec des projets de conception d'API dans Github : API et API de tourisme . N'hésitez pas à cloner ou à modifier selon vos besoins.