Usando Node.js, Express e Express Router para implementar uma API REST muito simples

Aug 23 2020

Para aprender as cordas do Node.js e do Express, estou tentando implementar uma API REST muito simples. A API diz o que deve fazer em vez de fazê-lo, para que eu possa me concentrar na estrutura do framework. O requisito para a API é o seguinte:

  • API simulada para um jogo de perguntas e respostas.
  • /qanda endpoint: Repositório de todos os pares de perguntas e respostas.
  • /qanda/questionId endpoint: Um par pergunta-resposta específico.
  • Deve lidar com verbos HTTP GET, POST, PUT, DELETE.
  • Suponha que o corpo da solicitação HTTP seja JSON.

Tudo isso é arbitrário. Estou apenas tentando entender como lidar e analisar solicitações e respostas HTTP. Para manter as coisas organizadas, também estou usando os módulos Express Router e Node.js.

Aqui está 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}`);
  });

E aqui está ./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;

Aguardo todos os tipos de feedback! Do estilo às melhores práticas, antipadrões, tudo o que você pode pensar é muito apreciado! Eu tenho algumas perguntas específicas:

  • O encadeamento de métodos é a maneira usual de lidar com algo assim no Express?
  • O estilo de encadeamento de métodos é legível e sustentável?
  • Existe uma maneira mais concisa de conseguir isso?
  • O que você faria de diferente?

Respostas

6 KaterAkeren Aug 23 2020 at 20:44

Depois de examinar os códigos que você escreveu, pude ver algumas coisas que não foram feitas corretamente. Abaixo estão algumas coisas que destaquei:

  • A estruturação da base de código não é adequada, portanto, pode ser mantida e escalável se a base de código crescer.
  • A convenção de nomenclatura da API RESTful não é aceitável. Os endpoints de qualquer API REST devem conter apenas recursos (substantivos) e usar métodos HTTP (verbos) para ações. Você nomeou seu terminal como /qanda em vez de /qandas . Eu recomendo que você leia estas diretrizes de design de API RESTful por Mahesh Haldar publicadas em Hackernoon. É um ótimo recurso para desenvolvedores.
  • Em terceiro lugar, você não separou seu servidor do expresso; ambos são acoplados juntos. Não é uma prática recomendada fazer isso.
  • Quando a API está sendo consumida por outros aplicativos, atualizar as APIs com algumas alterações também levaria à quebra do contrato de serviços existente - isso foi percebido por muitos aplicativos que fornecem serviços de API. É uma prática recomendada sempre prefixar todas as suas URLs com /api/v1/users . Se houver alguma atualização importante, podemos nomear o novo conjunto de APIs como v2, v3 ou v1.XX . respectivamente.

Pela minha experiência, é assim que eu abordaria:

  • Primeiramente, criarei um diretório chamado qanda-api , dentro dele, criarei outros diretórios chamados src e dois arquivos app.js e server.js que residem dentro do diretório raiz com src
  • Em segundo lugar, dentro do src , criarei dois subdiretórios chamados rotas e controladores
  • Em terceiro lugar, inicializarei um registro npm na raiz do diretório do projeto que rastreia todos os pacotes npm instalados digitando o comando abaixo através do terminal:

npm init

  • Após isso, instalarei as três dependências que irei utilizar através do mesmo terminal, mas desta vez você deverá ter acesso à internet:

npm install express dotenv morgan

  • Se o processo acima for executado com sucesso, você verá os pacotes instalados dentro de um arquivo chamado package.json

Agora vamos fazer a codificação colocando a mão na massa

  • Vamos primeiro criar um arquivo chamado controllers/qandaController.js em outro para escrever nossa lógica qanda que será exportada para seu arquivo de rota.
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'
    });
};
  • Agora é hora de criarmos nossa rota qanda em routes/qandaRoutes.js . Em seguida, importaremos a lógica do controlador em seu manipulador de rota abaixo.
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;
  • Vamos abrir o arquivo que criamos anteriormente chamado app.js em outro para fazer uso do express que havíamos instalado. O conteúdo deste arquivo é apenas para expresso que é exportado para seu servidor:
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;
  • Vamos criar nosso servidor que se torna nosso ponto de entrada para o aplicativo. É aqui que a execução real acontece quando o app.js exportado é usado inserindo-o como um argumento na função 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}`));

Conclusão

É altamente recomendável sempre separar as preocupações ao escrever códigos e ter uma base de código limpa que seja sustentável e escalável ao longo do tempo. Há mais na organização de código quando se trata de design de API, um deles é o princípio DRY (Don't Repeat Yourself). Se você quiser aprender mais sobre design de API em Node.JS, recomendo que você faça este curso do Jonas . Ele desmistifica os conceitos abstratos e ensina as melhores práticas ao núcleo. Eu envolvi esta resposta com projetos de design de API no Github: API e API de turismo . Sinta-se à vontade para clonar ou modificar conforme necessário.