Rotas Dinâmicas, Middleware Customizado, Estrutura de Projetos e APIs REST
🎯 Objetivo: Dominar Express.js avançado, organizar projetos profissionalmente e criar APIs REST completas
0% concluído
Express.js Avançado: Rotas Dinâmicas e Middleware Customizado
Aprenda a criar rotas flexíveis que capturam parâmetros da URL e query strings!
// Simulação de Express.js para demonstração
const express = {
routes: [],
get(path, handler) {
this.routes.push({ method: 'GET', path, handler });
console.log(`✅ Rota GET ${path} registrada`);
},
post(path, handler) {
this.routes.push({ method: 'POST', path, handler });
console.log(`✅ Rota POST ${path} registrada`);
},
put(path, handler) {
this.routes.push({ method: 'PUT', path, handler });
console.log(`✅ Rota PUT ${path} registrada`);
},
delete(path, handler) {
this.routes.push({ method: 'DELETE', path, handler });
console.log(`✅ Rota DELETE ${path} registrada`);
},
// Simular requisição
simulateRequest(method, url, body = null) {
console.log(`\n🌐 ${method} ${url}`);
// Encontrar rota correspondente
const route = this.routes.find(r => {
if (r.method !== method) return false;
// Converter rota Express para regex
const routeRegex = r.path
.replace(/:[^/]+/g, '([^/]+)')
.replace(/\//g, '\\/');
return new RegExp(`^${routeRegex}$`).test(url.split('?')[0]);
});
if (route) {
// Extrair parâmetros
const params = this.extractParams(route.path, url);
const query = this.extractQuery(url);
const req = { params, query, body };
const res = {
json: (data) => console.log('📤 Resposta JSON:', JSON.stringify(data, null, 2)),
send: (data) => console.log('📤 Resposta:', data),
status: (code) => ({
json: (data) => console.log(`📤 Status ${code}:`, JSON.stringify(data, null, 2)),
send: (data) => console.log(`📤 Status ${code}:`, data)
})
};
route.handler(req, res);
} else {
console.log('❌ Rota não encontrada - 404');
}
},
extractParams(routePath, url) {
const routeParts = routePath.split('/');
const urlParts = url.split('?')[0].split('/');
const params = {};
routeParts.forEach((part, index) => {
if (part.startsWith(':')) {
const paramName = part.slice(1);
params[paramName] = urlParts[index];
}
});
return params;
},
extractQuery(url) {
const queryString = url.split('?')[1];
if (!queryString) return {};
const query = {};
queryString.split('&').forEach(param => {
const [key, value] = param.split('=');
query[key] = decodeURIComponent(value || '');
});
return query;
}
};
// Banco de dados simulado
const usuarios = [
{ id: 1, nome: 'Ana Silva', email: 'ana@email.com', idade: 28 },
{ id: 2, nome: 'João Santos', email: 'joao@email.com', idade: 35 },
{ id: 3, nome: 'Maria Costa', email: 'maria@email.com', idade: 42 }
];
const posts = [
{ id: 1, titulo: 'Primeiro Post', conteudo: 'Conteúdo do primeiro post', autorId: 1 },
{ id: 2, titulo: 'Segundo Post', conteudo: 'Conteúdo do segundo post', autorId: 2 },
{ id: 3, titulo: 'Terceiro Post', conteudo: 'Conteúdo do terceiro post', autorId: 1 }
];
console.log('=== CONFIGURANDO ROTAS DINÂMICAS ===');
// 1. Rotas básicas
express.get('/', (req, res) => {
res.json({ message: 'API funcionando!', timestamp: new Date().toISOString() });
});
// 2. Listar todos os usuários com filtros
express.get('/usuarios', (req, res) => {
let resultado = [...usuarios];
// Filtrar por idade mínima
if (req.query.idadeMin) {
const idadeMin = parseInt(req.query.idadeMin);
resultado = resultado.filter(u => u.idade >= idadeMin);
}
// Filtrar por nome
if (req.query.nome) {
resultado = resultado.filter(u =>
u.nome.toLowerCase().includes(req.query.nome.toLowerCase())
);
}
res.json({
total: resultado.length,
filtros: req.query,
usuarios: resultado
});
});
// 3. Buscar usuário específico
express.get('/usuarios/:id', (req, res) => {
const id = parseInt(req.params.id);
const usuario = usuarios.find(u => u.id === id);
if (usuario) {
res.json(usuario);
} else {
res.status(404).json({ erro: 'Usuário não encontrado' });
}
});
// 4. Posts de um usuário específico
express.get('/usuarios/:id/posts', (req, res) => {
const autorId = parseInt(req.params.id);
const postsDoUsuario = posts.filter(p => p.autorId === autorId);
res.json({
autorId,
total: postsDoUsuario.length,
posts: postsDoUsuario
});
});
// 5. Buscar posts com filtros
express.get('/posts', (req, res) => {
let resultado = [...posts];
// Filtrar por autor
if (req.query.autor) {
const autorId = parseInt(req.query.autor);
resultado = resultado.filter(p => p.autorId === autorId);
}
// Buscar no título
if (req.query.busca) {
resultado = resultado.filter(p =>
p.titulo.toLowerCase().includes(req.query.busca.toLowerCase())
);
}
res.json({
total: resultado.length,
filtros: req.query,
posts: resultado
});
});
// 6. Post específico
express.get('/posts/:id', (req, res) => {
const id = parseInt(req.params.id);
const post = posts.find(p => p.id === id);
if (post) {
// Incluir informações do autor
const autor = usuarios.find(u => u.id === post.autorId);
res.json({
...post,
autor: autor ? { nome: autor.nome, email: autor.email } : null
});
} else {
res.status(404).json({ erro: 'Post não encontrado' });
}
});
console.log('\n=== TESTANDO ROTAS ===');
// Testando as rotas
express.simulateRequest('GET', '/');
express.simulateRequest('GET', '/usuarios');
express.simulateRequest('GET', '/usuarios?idadeMin=30');
express.simulateRequest('GET', '/usuarios?nome=ana');
express.simulateRequest('GET', '/usuarios/1');
express.simulateRequest('GET', '/usuarios/999');
express.simulateRequest('GET', '/usuarios/1/posts');
express.simulateRequest('GET', '/posts?autor=1');
express.simulateRequest('GET', '/posts?busca=primeiro');
express.simulateRequest('GET', '/posts/1');
Route Params (:id)
Parâmetros obrigatórios na URL
Query Strings (?nome=valor)
Parâmetros opcionais para filtros
Body (POST/PUT)
Dados enviados no corpo da requisição
Headers
Metadados da requisição
Middleware são funções que executam durante o ciclo de vida da requisição. Vamos criar nossos próprios!
// Sistema de middleware customizado
class ExpressSimulator {
constructor() {
this.middlewares = [];
this.routes = [];
}
// Registrar middleware global
use(middleware) {
this.middlewares.push(middleware);
console.log('🔧 Middleware registrado');
}
// Registrar rota com middleware específico
get(path, ...handlers) {
const middleware = handlers.slice(0, -1);
const handler = handlers[handlers.length - 1];
this.routes.push({
method: 'GET',
path,
middleware,
handler
});
console.log(`✅ Rota GET ${path} registrada com ${middleware.length} middleware(s)`);
}
// Simular requisição
async simulateRequest(method, url, headers = {}, body = null) {
console.log(`\n🌐 ${method} ${url}`);
console.log('📋 Headers:', headers);
const req = {
method,
url,
headers,
body,
params: {},
query: {},
user: null,
startTime: Date.now()
};
const res = {
statusCode: 200,
headers: {},
status(code) {
this.statusCode = code;
return this;
},
json(data) {
console.log(`📤 Status ${this.statusCode}:`, JSON.stringify(data, null, 2));
return this;
},
send(data) {
console.log(`📤 Status ${this.statusCode}:`, data);
return this;
},
setHeader(name, value) {
this.headers[name] = value;
}
};
let index = 0;
// Função next para passar para próximo middleware
const next = (error) => {
if (error) {
console.log('❌ Erro no middleware:', error.message);
return res.status(500).json({ erro: error.message });
}
// Executar middlewares globais
if (index < this.middlewares.length) {
const middleware = this.middlewares[index++];
return middleware(req, res, next);
}
// Encontrar e executar rota
const route = this.routes.find(r => {
if (r.method !== method) return false;
const routeRegex = r.path
.replace(/:[^/]+/g, '([^/]+)')
.replace(/\//g, '\\/');
return new RegExp(`^${routeRegex}$`).test(url.split('?')[0]);
});
if (route) {
// Extrair parâmetros
req.params = this.extractParams(route.path, url);
req.query = this.extractQuery(url);
// Executar middlewares da rota
let routeIndex = 0;
const nextRoute = (error) => {
if (error) {
console.log('❌ Erro no middleware da rota:', error.message);
return res.status(500).json({ erro: error.message });
}
if (routeIndex < route.middleware.length) {
const middleware = route.middleware[routeIndex++];
return middleware(req, res, nextRoute);
}
// Executar handler final
route.handler(req, res);
};
nextRoute();
} else {
res.status(404).json({ erro: 'Rota não encontrada' });
}
};
next();
}
extractParams(routePath, url) {
const routeParts = routePath.split('/');
const urlParts = url.split('?')[0].split('/');
const params = {};
routeParts.forEach((part, index) => {
if (part.startsWith(':')) {
const paramName = part.slice(1);
params[paramName] = urlParts[index];
}
});
return params;
}
extractQuery(url) {
const queryString = url.split('?')[1];
if (!queryString) return {};
const query = {};
queryString.split('&').forEach(param => {
const [key, value] = param.split('=');
query[key] = decodeURIComponent(value || '');
});
return query;
}
}
// Criar instância do simulador
const app = new ExpressSimulator();
console.log('=== CRIANDO MIDDLEWARES CUSTOMIZADOS ===');
// 1. Middleware de Log
const loggerMiddleware = (req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`📝 [${timestamp}] ${req.method} ${req.url}`);
req.startTime = Date.now();
next();
};
// 2. Middleware de Autenticação
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
console.log('🔒 Token não fornecido');
return res.status(401).json({ erro: 'Token de acesso necessário' });
}
// Simular validação de token
if (token === 'Bearer token-valido') {
req.user = { id: 1, nome: 'Usuário Logado', role: 'admin' };
console.log('✅ Usuário autenticado:', req.user.nome);
next();
} else {
console.log('❌ Token inválido');
res.status(401).json({ erro: 'Token inválido' });
}
};
// 3. Middleware de Validação
const validateUserMiddleware = (req, res, next) => {
if (req.method === 'POST' || req.method === 'PUT') {
const { nome, email } = req.body || {};
if (!nome || nome.length < 2) {
return res.status(400).json({ erro: 'Nome deve ter pelo menos 2 caracteres' });
}
if (!email || !email.includes('@')) {
return res.status(400).json({ erro: 'Email inválido' });
}
console.log('✅ Dados validados com sucesso');
}
next();
};
// 4. Middleware de Rate Limiting
const rateLimitMiddleware = (() => {
const requests = new Map();
return (req, res, next) => {
const ip = req.headers['x-forwarded-for'] || 'localhost';
const now = Date.now();
const windowMs = 60000; // 1 minuto
const maxRequests = 10;
if (!requests.has(ip)) {
requests.set(ip, []);
}
const userRequests = requests.get(ip);
// Remover requisições antigas
const validRequests = userRequests.filter(time => now - time < windowMs);
if (validRequests.length >= maxRequests) {
console.log(`🚫 Rate limit excedido para ${ip}`);
return res.status(429).json({ erro: 'Muitas requisições. Tente novamente em 1 minuto.' });
}
validRequests.push(now);
requests.set(ip, validRequests);
console.log(`📊 Rate limit: ${validRequests.length}/${maxRequests} para ${ip}`);
next();
};
})();
// 5. Middleware de Tempo de Resposta
const responseTimeMiddleware = (req, res, next) => {
const originalJson = res.json;
const originalSend = res.send;
res.json = function(data) {
const responseTime = Date.now() - req.startTime;
console.log(`⏱️ Tempo de resposta: ${responseTime}ms`);
res.setHeader('X-Response-Time', `${responseTime}ms`);
return originalJson.call(this, data);
};
res.send = function(data) {
const responseTime = Date.now() - req.startTime;
console.log(`⏱️ Tempo de resposta: ${responseTime}ms`);
res.setHeader('X-Response-Time', `${responseTime}ms`);
return originalSend.call(this, data);
};
next();
};
// Registrar middlewares globais
app.use(loggerMiddleware);
app.use(rateLimitMiddleware);
app.use(responseTimeMiddleware);
// Rotas com middlewares específicos
app.get('/', (req, res) => {
res.json({ message: 'API funcionando!', timestamp: new Date().toISOString() });
});
app.get('/publico', (req, res) => {
res.json({ message: 'Rota pública - sem autenticação necessária' });
});
app.get('/privado', authMiddleware, (req, res) => {
res.json({
message: 'Rota privada - acesso autorizado',
usuario: req.user
});
});
app.get('/admin', authMiddleware, (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ erro: 'Acesso negado - apenas administradores' });
}
next();
}, (req, res) => {
res.json({
message: 'Área administrativa',
usuario: req.user,
permissoes: ['criar', 'editar', 'deletar']
});
});
console.log('\n=== TESTANDO MIDDLEWARES ===');
// Testando as rotas
app.simulateRequest('GET', '/');
app.simulateRequest('GET', '/publico');
app.simulateRequest('GET', '/privado'); // Sem token
app.simulateRequest('GET', '/privado', { authorization: 'Bearer token-invalido' });
app.simulateRequest('GET', '/privado', { authorization: 'Bearer token-valido' });
app.simulateRequest('GET', '/admin', { authorization: 'Bearer token-valido' });
Crie uma API completa para gerenciar produtos com rotas dinâmicas e middleware de validação!
Teste seus conhecimentos em Express.js avançado
Crie uma rota que capture ID e categoria
Implemente middleware de autenticação
Construa CRUD completo com validação
Parâmetros, query strings e middleware customizado
express.Router, controllers e modularização
CRUD completo, validação e boas práticas
Na Semana 4, você aprenderá sobre MongoDB, Mongoose e persistência de dados!