Como organizar e manipular rotas com Node.js e Express
O Express.js é conhecido como o framework minimalista e flexível para Node.js, uma biblioteca muito utilizada para desenvolvimento de aplicativos web.
Neste artigo vamos falar sobre um recurso muito específico: organização e manipulação de rotas. Criaremos um aplicativo do zero usando rotas seguindo o padrão REST. Depois, vamos refatorar e manipular as rotas com o objetivo de dar maior esclarecimento sobre as várias possibilidades existentes para melhorar e organizar seus projetos.
Estrutura inicial para uma aplicação Node.js com Express
Para começarmos nosso projeto precisamos de um diretório onde serão salvos todos os arquivos específicos da nossa aplicação. Dentro deste diretório,deve haver um arquivo package.json,
que tem como propósito organizar e gerenciar as dependências do projeto.
Listei abaixo os comandos que executei no meu terminal para criar o diretório projeto
com o arquivo package.json
dentro.
mkdir -p ~/Desktop/projeto
cd ~/Desktop/projeto
touch package.json
Não faz parte do escopo deste artigo explorar o que faz o arquivo package.json
. Por isso apenas copie e cole no package.json
o conteúdo que vou disponibilizar em seguida.
{
"name": "workspace",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon app.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.12"
},
"dependencies": {
"express": "^4.17.1"
}
}
Vamos criar agora o arquivo app.js
– segue abaixo o comando que executei no terminal para criá-lo.
touch app.js
Dentro do arquivo app.js,
você deverá copiar e colar o código fonte que será disponibilizado a seguir. Essa aplicação já utiliza a biblioteca Express e vai ser executada na porta 3000.
const express = require('express');
const app = express();
app.use(express.json());
app.get('/', (req, res) => {
res.send('hello, world!');
});
app.listen(3000, () => {
console.log('our app is running locally...');
});
Mas ainda não terminamos. Vamos baixar as dependências,que no nosso caso são o Express.js e a biblioteca Nodemon. Para isso, execute o seguinte comando no terminal.
npm install
Para executar a aplicação de forma que fique disponível pelo navegador através do endereço http://localhost:3000/ ,
precisaremos executar o seguinte comando pelo terminal.
npm start
Para validar se está tudo funcionando,acesse seu navegador, digite na barra de endereços o caminho http://localhost:3000/
e valide se foi exibido uma página web contendo o texto hello, world!
.
Como criar rotas seguindo o padrão REST
Hoje, é muito comum que as rotas de nossas aplicações sejam criadas seguindo o padrão REST. No entanto, não é o objetivo deste artigo entrar em detalhes sobre o que é REST e seus benefícios. O foco é falar do uso das rotas e como organizá-las dentro do seu projeto – mas toda rota que eu criada neste artigo está seguindo cuidadosamente o padrão REST.
Considere que neste projeto existem dois recursos que serão gerenciados pela nossa aplicação. Esses recursos são Aluno e Curso. Cada um deles possui seu próprio endpoint, onde serão gerenciados várias operações possíveis, como : criar, alterar, remover e recuperar alunos e/ou cursos.
Segue abaixo como ficou o arquivo app.js
quando adicionei as rotas específicas para gerenciamento de Alunos e Cursos.
const express = require('express');
const app = express();
app.use(express.json());
app.get('/api/aluno', (req, res) => {
res.status(200).json({
status: 'success',
data: {
alunos: [
{
id: 1,
nome: 'Fulano Silva',
idade: 27
},
{
id: 2,
nome: 'Ciclano Almeida',
idade: 32
}
]
}
});
});
app.get('/api/aluno/:id', (req, res) => {
res.status(200).json({
status: 'success',
data: {
aluno: {
id: req.params.id,
nome: 'Fulano Silva',
idade: 27
}
}
});
});
app.post('/api/aluno', (req, res) => {
const alunoId = Math.floor(Math.random() * 10);
const aluno = Object.assign({ id: alunoId }, req.body);
res.status(200).json({
status: 'success',
data: {
aluno
}
});
});
app.patch('/api/aluno/:id', (req, res) => {
const alunoId = req.params.id;
const aluno = Object.assign({ id: alunoId }, req.body);
res.status(200).json({
status: 'success',
data: {
aluno
}
});
});
app.delete('/api/aluno/:id', (req, res) => {
res.status(204).json({
status: 'success',
data: null,
});
});
app.get('/api/curso', (req, res) => {
res.status(200).json({
status: 'success',
data: {
cursos: [
{
id: 1,
nome: 'Curso de Node.js'
},
{
id: 2,
nome: 'Curso de React.js'
}
]
}
});
});
app.get('/api/curso/:id', (req, res) => {
res.status(200).json({
status: 'success',
data: {
curso: {
id: req.params.id,
nome: 'Curso de React.js'
}
}
});
});
app.post('/api/curso', (req, res) => {
const cursoId = Math.floor(Math.random() * 10);
const curso = Object.assign({ id: cursoId }, req.body);
res.status(200).json({
status: 'success',
data: {
curso
}
});
});
app.patch('/api/curso/:id', (req, res) => {
const cursoId = req.params.id;
const curso = Object.assign({ id: cursoId }, req.body);
res.status(200).json({
status: 'success',
data: {
curso
}
});
});
app.delete('/api/curso/:id', (req, res) => {
res.status(204).json({
status: 'success',
data: null,
});
});
app.listen(3000, () => {
console.log('our app is running locally...');
});
Assim como eu, você também achou que o app.js
está um pouco longo demais? Caso sim, então me acompanhe um pouco mais na leitura do artigo. O objetivo deste artigo é exatamente mostrar um cenário com muito espaço para melhorias e levar este código para um próximo nível! 😛
Como mover funcionalidade para os controllers
Vamos dar início à nossa primeira refatoração no arquivo app.js
, tomando algumas idéias do padrão MVC emprestados. Vamos criar dois arquivos para onde vamos mover toda lógica existente nas rotas.
Para isso, crie uma pasta chamada controllers,
contendo dentro dois arquivos: alunoController.js
e cursoController.js
. Segue os comandos abaixo para que você digite no seu terminal.
mkdir -p controllers
touch controllers/alunoController.js
touch controllers/cursoController.js
Agora toda funcionalidade específica para as rotas de alunos deverão ser movidos para o arquivo alunoController.js
. Abaixo segue como ficou o resultado final.
exports.getAll = (req, res) => {
res.status(200).json({
status: 'success',
data: {
alunos: [
{
id: 1,
nome: 'Fulano Silva',
idade: 27
},
{
id: 2,
nome: 'Ciclano Almeida',
idade: 32
}
]
}
});
};
exports.getOne = (req, res) => {
res.status(200).json({
status: 'success',
data: {
aluno: {
id: req.params.id,
nome: 'Fulano Silva',
idade: 27
}
}
});
};
exports.createOne = (req, res) => {
const alunoId = Math.floor(Math.random() * 10);
const aluno = Object.assign({ id: alunoId }, req.body);
res.status(200).json({
status: 'success',
data: {
aluno
}
});
};
exports.updateOne = (req, res) => {
const alunoId = req.params.id;
const aluno = Object.assign({ id: alunoId }, req.body);
res.status(200).json({
status: 'success',
data: {
aluno
}
});
};
exports.deleteOne = (req, res) => {
res.status(204).json({
status: 'success',
data: null,
});
};
Assim como movemos toda funcionalidade específica de alunos, precisamos repetir o mesmo para as funcionalidades relacionadas a cursos em cursoController.js
.
exports.getAll = (req, res) => {
res.status(200).json({
status: 'success',
data: {
alunos: [
{
id: 1,
nome: 'Curso de Node.js'
},
{
id: 2,
nome: 'Curso de MongoDB'
}
]
}
});
};
exports.getOne = (req, res) => {
res.status(200).json({
status: 'success',
data: {
aluno: {
id: req.params.id,
nome: 'Curso de Node.js'
}
}
});
};
exports.createOne = (req, res) => {
const cursoId = Math.floor(Math.random() * 10);
const curso = Object.assign({ id: cursoId }, req.body);
res.status(200).json({
status: 'success',
data: {
curso
}
});
};
exports.updateOne = (req, res) => {
const cursoId = req.params.id;
const curso = Object.assign({ id: cursoId }, req.body);
res.status(200).json({
status: 'success',
data: {
curso
}
});
};
exports.deleteOne = (req, res) => {
res.status(204).json({
status: 'success',
data: null,
});
};
Agora que toda a lógica específica para a execução das rotas foi movido para seu próprio arquivo, fica muito simples melhorar significativamente o código que está no app.js
. Analise no código abaixo como importei os modulos alunoController
e cursoController
. Depois, registrei as rotas reusando as funções que importamos.
const express = require('express');
const alunoController = require('./controllers/alunoController');
const cursoController = require('./controllers/cursoController');
const app = express();
app.use(express.json());
app.get('/api/aluno', alunoController.getAll);
app.get('/api/aluno/:id', alunoController.getOne);
app.post('/api/aluno', alunoController.createOne);
app.patch('/api/aluno/:id', alunoController.updateOne);
app.delete('/api/aluno/:id', alunoController.deleteOne);
app.get('/api/curso', cursoController.getAll);
app.get('/api/curso/:id', cursoController.getOne);
app.post('/api/curso', cursoController.createOne);
app.patch('/api/curso/:id', cursoController.updateOne);
app.delete('/api/curso/:id', cursoController.deleteOne);
app.listen(3000, () => {
console.log('our app is running locally...');
});
Como agrupar rotas usando Express.js
Precisamos de um próximo passo: repetir /api/curso
e /api/aluno
sempre que registro uma rota. Para isso criaremos um objeto do tipo Router
executando o método express.Router()
.
const alunoRouter = express.Router();
const cursoRouter = express.Router();
O retorno de express.Router()
além de ser um objeto também é um Middleware, ou seja, para aplicar estas rotas na sua aplicação Express.js em algum momento você deve registrá-lo utilizando o método express.use(router)
.
const alunoRouter = express.Router();
const cursoRouter = express.Router();
// ...
app.use(alunoRouter);
app.use(cursoRouter);
Agora que você entendeu como se cria uma instância de Router
e como registrá-lo a stack de middlewares do Express , vamos ao código.Abaixo vemos como ficou o bloco que contém as rotas já agrupadas e específicas para alunos.
const alunoRouter = express.Router();
alunoRouter
.route('/api/aluno')
.get(alunoController.getAll)
.post(alunoController.createOne);
alunoRouter
.route('/api/aluno/:id')
.get(alunoController.getOne)
.patch(alunoController.updateOne)
.delete(alunoController.deleteOne);
Podemos ver a seguir como ficou o bloco que contém as rotas já agrupadas e específicas para cursos.
const cursoRouter = express.Router();
cursoRouter.route('/api/curso')
.get(cursoController.getAll)
.post(cursoController.createOne);
cursoRouter.route('/api/curso/:id')
.get(cursoController.getOne)
.patch(cursoController.updateOne)
.delete(cursoController.deleteOne);
Finalizamos mais uma refatoração significativa até aqui. Segue abaixo como ficou o resultado final do arquivo app.js
– para que fique claro para você como que todas estas peças se encaixam.
const express = require('express');
const alunoController = require('./controllers/alunoController');
const cursoController = require('./controllers/cursoController');
const app = express();
app.use(express.json());
const alunoRouter = express.Router();
alunoRouter
.route('/api/aluno')
.get(alunoController.getAll)
.post(alunoController.createOne);
alunoRouter
.route('/api/aluno/:id')
.get(alunoController.getOne)
.patch(alunoController.updateOne)
.delete(alunoController.deleteOne);
const cursoRouter = express.Router();
cursoRouter.route('/api/curso')
.get(cursoController.getAll)
.post(cursoController.createOne);
cursoRouter.route('/api/curso/:id')
.get(cursoController.getOne)
.patch(cursoController.updateOne)
.delete(cursoController.deleteOne);
app.use(alunoRouter);
app.use(cursoRouter);
app.listen(3000, () => {
console.log('our app is running locally...');
});
Como modularizar a criação das rotas
Que tal modularizar um pouco mais o nosso projeto movendo a criação das rotas para um módulo à parte. Vamos criar uma pasta chamada routes,
que deverá conter dentro os arquivos alunoRoutes.js
e cursoRoutes.js
.
mkdir routes
touch routes/alunoRoutes.js
touch routes/cursoRoutes.js
Mova para dentro do arquivo alunoRoutes.js
toda lógica onde registramos as rotas para gestão dos alunos.
const express = require('express');
const alunoController = require('./../controllers/alunoController');
const router = express.Router();
router
.route('/api/aluno')
.get(alunoController.getAll)
.post(alunoController.createOne);
router
.route('/api/aluno/:id')
.get(alunoController.getOne)
.patch(alunoController.updateOne)
.delete(alunoController.deleteOne);
module.exports = router;
O mesmo deve ser feito para cursos, ou seja, mova para dentro do arquivo cursoRoutes.js
toda lógica onde registramos as rotas para gestão dos cursos.
const express = require('express');
const cursoController = require('./../controllers/cursoController');
const router = express.Router();
router
.route('/api/curso')
.get(cursoController.getAll)
.post(cursoController.createOne);
router
.route('/api/curso/:id')
.get(cursoController.getOne)
.patch(cursoController.updateOne)
.delete(cursoController.deleteOne);
module.exports = router;
Para finalizar,. precisamos dar uma último refactoring no nosso app.js
. Apenas remova toda lógica relacionada a criação das rotas para preferir importá-las. Abaixo podemos conferir o código em app.js
já finalizado.
const express = require('express');
const alunoRouter = require('./routes/alunoRoutes');
const cursoRouter = require('./routes/cursoRoutes');
const app = express();
app.use(express.json());
app.use(alunoRouter);
app.use(cursoRouter);
app.listen(3000, () => {
console.log('our app is running locally...');
});
Considerações finais
Obrigado por acompanhar o artigo até aqui. Vimos como começar um projeto de forma simples usando Node.js e Express.js, para então refatorar todo o código de forma a organizar as rotas de forma cada vez melhor. Desta maneira, o projeto fica mais limpo e fácil de dar manutenção. Espero que o que você aprendeu aqui seja útil para você organizar mais efetivamente as rotas em seus próprios projetos ou até mesmo, quem sabe, descobrir novas possibilidades e caminhos para manter o seu código limpo, bonito e organizado.
Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:
About author
Você pode gostar também
Descubra como o RabbitMQ pode otimizar o processamento de dados em seu sistema web
O RabbitMQ é um software de controle e gerenciamento de filas de mensagens. Ele recebe e armazena mensagens em filas até que alguém solicite essas mensagens. As mensagens podem ser
Como criar um chat com PHP e Swoole: um guia passo a passo
Quando um desenvolvedor recebe a tarefa de implementar um chat dificilmente o PHP será a linguagem escolhida. Isso ocorre por alguns motivos, como por exemplo, dificilmente vermos o uso de
Acelere seus algoritmos de Machine Learning com CUDA no Linux
Se você deseja trabalhar com algoritmos de Machine Learning, provavelmente precisará usar processamento paralelo para acelerar os resultados dos seus algoritmos. Muitos frameworks como por exemplo, o TensorFlow, já possuem