Aprenda a organizar rotas em módulos, implementar middleware personalizado e tratar parâmetros de rota e query
Imagine que você está organizando uma empresa que cresceu muito rapidamente. No início, você fazia tudo sozinho em uma sala pequena. Mas agora, com mais funcionários e mais responsabilidades, você precisa criar departamentos específicos e regras claras.
É exatamente isso que acontece com aplicações web! Quando começamos, tudo fica no mesmo arquivo, mas conforme cresce, precisamos organizar melhor. Hoje você vai aprender a criar "departamentos" (rotas organizadas) e "porteiros" (middleware) para sua aplicação.
Imagine uma empresa que começou pequena, com o dono fazendo tudo: vendas, contabilidade, recursos humanos, tudo na mesma mesa. Conforme a empresa cresce, ela precisa criar departamentos específicos:
Cada departamento tem suas próprias regras, mas todos fazem parte da mesma empresa!
O Express Router é como criar departamentos na sua aplicação. Cada "departamento" (router) cuida de um tipo específico de funcionalidade, mas todos fazem parte da mesma aplicação.
Isso torna seu código mais organizado, fácil de manter e permite que diferentes pessoas trabalhem em departamentos diferentes sem conflitos.
// Criando um "departamento" de produtos
const express = require('express');
const router = express.Router();
// Este departamento cuida apenas de produtos
router.get('/', (req, res) => {
res.json({ message: 'Lista de produtos' });
});
router.post('/', (req, res) => {
res.json({ message: 'Produto criado' });
});
module.exports = router;
Você vai criar uma API para uma lanchonete. A lanchonete tem 3 departamentos: Cardápio, Pedidos e Clientes.
routescardapio.js, pedidos.js, clientes.jsapp.js, importe e use todos os routerslanchonete/
├── app.js
└── routes/
├── cardapio.js # GET /cardapio, POST /cardapio
├── pedidos.js # GET /pedidos, POST /pedidos
└── clientes.js # GET /clientes, POST /clientes
Uma boa organização de rotas é fundamental para manter o código limpo e escalável. Vamos ver diferentes padrões de organização.
projeto/
├── app.js
├── routes/
│ ├── index.js # Rotas principais
│ ├── usuarios.js # Rotas de usuários
│ ├── produtos.js # Rotas de produtos
│ └── auth.js # Rotas de autenticação
├── controllers/
│ ├── usuarioController.js
│ └── produtoController.js
├── middleware/
│ ├── auth.js
│ └── validation.js
└── utils/
└── helpers.js
const express = require('express');
const router = express.Router();
// Importar outros routers
const usuariosRouter = require('./usuarios');
const produtosRouter = require('./produtos');
const authRouter = require('./auth');
// Rota principal
router.get('/', (req, res) => {
res.json({
message: 'API da Padaria Doce Sabor',
version: '1.0.0',
endpoints: {
usuarios: '/api/usuarios',
produtos: '/api/produtos',
auth: '/api/auth'
}
});
});
// Usar sub-routers
router.use('/usuarios', usuariosRouter);
router.use('/produtos', produtosRouter);
router.use('/auth', authRouter);
module.exports = router;
const express = require('express');
const router = express.Router();
const produtoController = require('../controllers/produtoController');
const { validarProduto } = require('../middleware/validation');
// GET /api/produtos - Listar todos os produtos
router.get('/', produtoController.listarTodos);
// GET /api/produtos/:id - Buscar produto por ID
router.get('/:id', produtoController.buscarPorId);
// POST /api/produtos - Criar novo produto
router.post('/', validarProduto, produtoController.criar);
// PUT /api/produtos/:id - Atualizar produto
router.put('/:id', validarProduto, produtoController.atualizar);
// DELETE /api/produtos/:id - Deletar produto
router.delete('/:id', produtoController.deletar);
// GET /api/produtos/categoria/:categoria - Filtrar por categoria
router.get('/categoria/:categoria', produtoController.buscarPorCategoria);
module.exports = router;
// Simulando um banco de dados em memória
let produtos = [
{ id: 1, nome: 'Pão Francês', preco: 0.50, categoria: 'paes' },
{ id: 2, nome: 'Croissant', preco: 3.50, categoria: 'doces' },
{ id: 3, nome: 'Coxinha', preco: 4.00, categoria: 'salgados' }
];
const produtoController = {
listarTodos: (req, res) => {
const { categoria, preco_min, preco_max } = req.query;
let resultado = produtos;
// Filtrar por categoria
if (categoria) {
resultado = resultado.filter(p => p.categoria === categoria);
}
// Filtrar por preço
if (preco_min) {
resultado = resultado.filter(p => p.preco >= parseFloat(preco_min));
}
if (preco_max) {
resultado = resultado.filter(p => p.preco <= parseFloat(preco_max));
}
res.json({
produtos: resultado,
total: resultado.length
});
},
buscarPorId: (req, res) => {
const id = parseInt(req.params.id);
const produto = produtos.find(p => p.id === id);
if (!produto) {
return res.status(404).json({ error: 'Produto não encontrado' });
}
res.json(produto);
},
criar: (req, res) => {
const { nome, preco, categoria } = req.body;
const novoProduto = {
id: produtos.length + 1,
nome,
preco: parseFloat(preco),
categoria
};
produtos.push(novoProduto);
res.status(201).json(novoProduto);
},
atualizar: (req, res) => {
const id = parseInt(req.params.id);
const index = produtos.findIndex(p => p.id === id);
if (index === -1) {
return res.status(404).json({ error: 'Produto não encontrado' });
}
produtos[index] = { ...produtos[index], ...req.body };
res.json(produtos[index]);
},
deletar: (req, res) => {
const id = parseInt(req.params.id);
const index = produtos.findIndex(p => p.id === id);
if (index === -1) {
return res.status(404).json({ error: 'Produto não encontrado' });
}
produtos.splice(index, 1);
res.status(204).send();
},
buscarPorCategoria: (req, res) => {
const categoria = req.params.categoria;
const resultado = produtos.filter(p => p.categoria === categoria);
res.json({
categoria,
produtos: resultado,
total: resultado.length
});
}
};
module.exports = produtoController;
Imagine que você é um entregador e precisa encontrar casas em uma cidade. Existem diferentes tipos de "endereços" que você pode receber:
Na web, temos a mesma coisa! Algumas URLs são fixas, outras mudam dependendo do que queremos buscar, e podemos passar "instruções extras" também.
Parâmetros de Rota são como "variáveis" no endereço da URL. Eles permitem que uma única rota atenda diferentes requisições:
/usuario/123 - busca o usuário com ID 123/usuario/456 - busca o usuário com ID 456/produtos?categoria=doces - filtra produtos doces/produtos?categoria=salgados&preco=5 - filtra por categoria e preço// Rota com parâmetro dinâmico
app.get('/cliente/:nome', (req, res) => {
const nomeCliente = req.params.nome;
res.json({ mensagem: `Olá, ${nomeCliente}!` });
});
// Rota com filtros opcionais
app.get('/produtos', (req, res) => {
const categoria = req.query.categoria || 'todos';
res.json({ categoria, produtos: [] });
});
Você vai criar um sistema de delivery que precisa de diferentes tipos de "endereços":
/cliente/:id/pedido/:numero/restaurantes?bairro=centro&tipo=pizza/cliente/:id/cliente/:id/pedidos/restaurantes?bairro=X&tipo=Y&preco_max=ZGET /cliente/123
GET /cliente/123/pedidos
GET /restaurantes?bairro=centro
GET /restaurantes?bairro=centro&tipo=pizza&preco_max=30
Imagine um prédio comercial com vários andares e empresas. Antes de qualquer pessoa chegar ao seu destino final, ela precisa passar por diferentes "checkpoints":
Cada "porteiro" tem uma função específica, mas todos trabalham em sequência para garantir que apenas pessoas autorizadas cheguem ao destino certo!
Middleware são como "porteiros digitais" que interceptam todas as requisições antes delas chegarem ao destino final. Cada middleware pode:
// Um "porteiro" que registra todas as visitas
const porteiro = (req, res, next) => {
console.log(`${new Date().toLocaleTimeString()} - Alguém acessou: ${req.url}`);
next(); // Libera a pessoa para continuar
};
// Usar o porteiro em todas as rotas
app.use(porteiro);
Você vai criar um sistema de segurança para uma biblioteca digital. Precisa de 3 tipos de "porteiros":
/livros// Estrutura sugerida
middleware/
├── logger.js # Registra acessos
├── auth.js # Verifica carteirinha
└── rateLimit.js # Limita requisições
// No app.js
app.use('/livros', logger, auth, rateLimit, livrosRouter);
Imagine uma fábrica de brinquedos. Antes de qualquer produto sair da linha de produção e chegar às lojas, ele precisa passar por rigorosos testes de qualidade:
Se algum teste falhar, o produto volta para correção. Só produtos aprovados em TODOS os testes podem seguir adiante!
Validação de Dados é como ter inspetores de qualidade na sua API. Antes de processar qualquer informação, você verifica se ela está correta:
Se algo estiver errado, você rejeita os dados e explica o que precisa ser corrigido!
// Inspetor de qualidade para cadastro de usuário
const validarUsuario = (req, res, next) => {
const { nome, email, idade } = req.body;
const erros = [];
if (!nome || nome.length < 2) {
erros.push('Nome deve ter pelo menos 2 caracteres');
}
if (!email || !email.includes('@')) {
erros.push('Email deve ser válido');
}
if (!idade || idade < 18) {
erros.push('Idade deve ser maior que 18 anos');
}
if (erros.length > 0) {
return res.status(400).json({ erros });
}
next(); // Aprovado no controle de qualidade!
};
Você vai criar um sistema de controle de qualidade para cadastro de produtos em um e-commerce. Cada produto precisa passar por 4 "inspetores":
validarProduto que verifica todos os campos// Produto válido
{
"nome": "Smartphone XYZ",
"preco": 899.99,
"categoria": "eletronicos",
"estoque": 50
}
// Produto inválido
{
"nome": "AB", // Muito curto
"preco": -100, // Negativo
"categoria": "carros", // Categoria inválida
"estoque": "muitos" // Não é número
}
Agora que você aprendeu todos os conceitos com analogias, é hora de aplicar tudo em projetos reais! Você vai criar 3 sistemas diferentes, cada um focando em aspectos específicos que aprendemos.
💡 Dica: Comece pelo exercício que mais te interessar. Não precisa fazer todos de uma vez!
Você foi contratado para criar a API de uma lanchonete. O sistema precisa gerenciar cardápio, pedidos e clientes de forma organizada.
GET /api/cardapio - Listar todos os produtos
GET /api/cardapio/:id - Buscar produto específico
GET /api/cardapio/categoria/:cat - Produtos por categoria
POST /api/cardapio - Adicionar novo produto
PUT /api/cardapio/:id - Atualizar produto
DELETE /api/cardapio/:id - Remover produto
POST /api/pedidos - Criar novo pedido
GET /api/pedidos/:clienteId - Pedidos de um cliente
GET /api/pedidos?status=... - Filtrar por status
POST /api/clientes - Cadastrar cliente
GET /api/clientes/:id - Dados do cliente
Uma biblioteca precisa de um sistema para gerenciar livros, usuários e empréstimos. O sistema deve ter controles rigorosos de segurança e validação.
Implemente um sistema de "endereços" (parâmetros) inteligente:
GET /api/livros/autor/:nome - Livros de um autor
GET /api/livros/ano/:ano - Livros de um ano
GET /api/livros/genero/:genero - Livros por gênero
GET /api/livros/buscar?q=...&ano=...&autor=... - Busca avançada
GET /api/emprestimos/usuario/:id - Empréstimos de um usuário
GET /api/emprestimos/vencidos - Empréstimos em atraso
GET /api/emprestimos/hoje - Empréstimos que vencem hoje
Crie uma API completa para um e-commerce de produtos eletrônicos. Este exercício combina TODOS os conceitos aprendidos.
// Produtos com filtros avançados
GET /api/produtos?categoria=smartphones&preco_min=500&preco_max=2000
GET /api/produtos?marca=apple&ordenar=preco&direcao=asc
GET /api/produtos/categoria/:cat/marca/:marca
GET /api/produtos/promocao/:desconto // Ex: /promocao/20 (20% off)
// Carrinho personalizado por usuário
GET /api/carrinho/:usuarioId
POST /api/carrinho/:usuarioId/adicionar/:produtoId
DELETE /api/carrinho/:usuarioId/remover/:produtoId
// Pedidos com status e filtros
GET /api/pedidos/usuario/:id/status/:status
GET /api/pedidos/periodo/:dataInicio/:dataFim
GET /api/pedidos/valor/:min/:max
projeto/
├── app.js
├── routes/
│ ├── index.js
│ ├── produtos.js
│ └── usuarios.js
├── middleware/
│ ├── auth.js
│ ├── validation.js
│ └── logger.js
└── data/
└── produtos.json
🎯 Meta: Escolha UM exercício e complete-o totalmente antes de partir para o próximo. É melhor fazer um projeto completo do que três pela metade!
Aprenda a integrar sua aplicação com Supabase para persistência de dados
Ir para próxima aula