Como organizar e manipular rotas com Node.js e Express

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:

CURSOSCONSULTORIA

Anterior Como desenvolver dentro de um container usando VSCode Remote Containers
Próxima Consultoria 4Linux: A retomada da economia somada ao conhecimento da sua empresa

About author

Você pode gostar também

Desenvolvimento

Novo Angular 4.0: o que mudará no seu dia a dia?

Neste artigo falo sobre algumas das novidades da nova versão do Angular e explico o que aconteceu com a versão 3. Confira! Compartilhe este post: Share on Twitter Share on

Desenvolvimento

Zend Framework conheça os benefícios da ferramenta e porque usá-la!

Se você está lendo este artigo provavelmente você ja desenvolveu ou pretende desenvolver algum projeto de software, talvez você nunca tenha usado um framework antes por isso acho necessário uma

Segurança

OWASP, SQL Injection e consultas parametrizadas

Neste artigo, vamos entender o motivo pelo qual as injeções de dados (SQL Injections) estão no Top 10 da OWASP e o porquê dessa classe de vulnerabilidades precisar de atenção