Usando Node.js, Express e Express Router para implementar uma API REST muito simples
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
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.