Utilizzo di Node.js, Express ed Express Router per implementare un'API REST molto semplice

Aug 23 2020

Per imparare le corde di Node.js ed Express, sto cercando di implementare un'API REST molto semplice. L'API dice cosa dovrebbe fare invece di farlo, quindi posso concentrarmi sulla struttura del framework. Il requisito per l'API è il seguente:

  • Mock API per un gioco di domande e risposte.
  • /qanda endpoint: repository di tutte le coppie domanda-risposta.
  • /qanda/questionId endpoint: una specifica coppia domanda-risposta.
  • Dovrebbe gestire i verbi HTTP GET, POST, PUT, DELETE.
  • Supponiamo che il corpo della richiesta HTTP sia JSON.

Tutto questo è arbitrario. Sto solo cercando di imparare a gestire e analizzare le richieste e le risposte HTTP. Per mantenere le cose organizzate, utilizzo anche i moduli Express Router e Node.js.

Ecco 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}`);
  });

Ed ecco ./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;

Non vedo l'ora di ricevere tutti i tipi di feedback! Dallo stile, alle migliori pratiche, agli anti-pattern, qualunque cosa tu possa pensare è molto apprezzata! Ho alcune domande specifiche:

  • Il metodo concatena il solito modo per gestire qualcosa di simile in Express?
  • Lo stile di concatenamento del metodo è leggibile e gestibile?
  • C'è un modo più conciso per raggiungere questo obiettivo?
  • Cosa faresti diversamente?

Risposte

6 KaterAkeren Aug 23 2020 at 20:44

Dopo aver esaminato i codici che hai scritto, ho potuto vedere alcune cose che non sono state fatte correttamente. Di seguito sono riportate le poche cose che ho evidenziato:

  • La strutturazione della base di codice non va bene, quindi può essere gestibile e scalabile se la base di codice cresce.
  • La convenzione di denominazione dell'API RESTful non è accettabile. Gli endpoint di qualsiasi API REST devono contenere solo risorse (nomi) e utilizzare metodi HTTP (verbi) per le azioni. Hai chiamato il tuo endpoint come /qanda invece di /qandas . Ti consiglio di leggere queste linee guida per la progettazione di API RESTful di Mahesh Haldar pubblicate su Hackernoon. È una grande risorsa per gli sviluppatori.
  • In terzo luogo, non hai separato il tuo server da express; sono entrambi accoppiati insieme. Non è la migliore pratica farlo.
  • Quando l'API viene utilizzata da altre applicazioni, l'aggiornamento delle API con alcune modifiche porterebbe anche alla rottura del contratto di servizi esistente: questo è stato notato da molte applicazioni che forniscono servizi API. È buona norma aggiungere sempre il prefisso a tutti gli URL /api/v1/users . Se è presente un importante aggiornamento di rottura, possiamo denominare il nuovo set di API come v2, v3 o v1.XX . rispettivamente.

Dalla mia esperienza, questo è il modo in cui mi avvicinerei:

  • In primo luogo, creerò una directory chiamata, qanda-api , al suo interno, creerò altre directory chiamate src e due file app.js e server.js che risiedono all'interno della directory principale con src
  • In secondo luogo, all'interno di src creerò due sottodirectory chiamate route e controllers
  • In terzo luogo, inizializzerò un registro npm nella root della directory del progetto che tiene traccia di tutti i pacchetti installati da npm digitando il comando seguente tramite il terminale:

npm iniz

  • Successivamente, installerò le tre dipendenze che userò tramite lo stesso terminale, ma questa volta devi avere accesso a Internet:

npm install express dotenv morgan

  • Se il processo precedente viene eseguito correttamente, vedrai i pacchetti installati all'interno di un file chiamato package.json

Ora facciamo la codifica sporcandoci le mani

  • Creiamo prima un file chiamato controllers/qandaController.js in other per scrivere la nostra logica qanda che verrà esportata nel suo file di 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'
    });
};
  • Ora è il momento di creare la nostra rotta qanda in routes/qandaRoutes.js . Importeremo quindi la logica del controller nel suo gestore di route di seguito.
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;
  • Apriamo il file che abbiamo creato in precedenza chiamato app.js in altro per utilizzare l'express che avevamo installato. Il contenuto di questo file è solo per express che viene poi esportato sul suo server:
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;
  • Creiamo il nostro server che diventa il nostro punto di accesso all'applicazione. Qui avviene l'esecuzione effettiva per cui l' app.js esportato viene utilizzato inserendolo come argomento nella funzione 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}`));

Conclusione

Si consiglia vivamente di mantenere sempre la separazione delle preoccupazioni durante la scrittura dei codici e di disporre in modo appropriato di una base di codice pulita che sia gestibile e scalabile nel tempo. C'è di più nell'organizzazione del codice quando si tratta di progettazione di API, uno di questi è il principio DRY (Don't Repeat Yourself). Se vuoi saperne di più sulla progettazione delle API in Node.JS ti consiglio di seguire questo corso di Jonas . Demistifica i concetti astratti e insegna le migliori pratiche fino in fondo. Ho racchiuso questa risposta con i progetti di progettazione API in Github: API e Tourism API . Sentiti libero di clonare o modificare di cui hai bisogno.