Express.js: Fundamentos e Arquitetura
Express.js é um framework web minimalista para Node.js que facilita a criação de servidores HTTP e APIs REST. Sua força reside na simplicidade: você constrói apenas o que precisa, sem abstrações desnecessárias. O framework é construído sobre o módulo nativo http do Node.js, adicionando camadas de roteamento, middleware e utilitários que aceleram o desenvolvimento.
Para começar, instale Express via npm: npm install express. Um servidor básico requer apenas alguns comandos para escutar requisições. A arquitetura do Express é orientada por middlewares — funções que processam requisições antes de chegarem à rota final. Compreender esse fluxo é essencial para dominar o framework.
const express = require('express');
const app = express();
app.listen(3000, () => {
console.log('Servidor rodando na porta 3000');
});
Roteamento: O Coração das APIs
Definindo Rotas Básicas
Rotas definem como sua API responde a diferentes URLs e métodos HTTP. Express fornece métodos diretos para GET, POST, PUT, DELETE e outros. Cada rota é composta por um caminho, um método HTTP e uma função callback que recebe request e response.
const express = require('express');
const app = express();
// GET
app.get('/usuarios', (req, res) => {
res.json({ message: 'Lista de usuários' });
});
// POST
app.post('/usuarios', (req, res) => {
res.status(201).json({ message: 'Usuário criado' });
});
// PUT
app.put('/usuarios/:id', (req, res) => {
res.json({ message: `Usuário ${req.params.id} atualizado` });
});
// DELETE
app.delete('/usuarios/:id', (req, res) => {
res.status(204).send();
});
app.listen(3000);
Parâmetros e Query Strings
Existem três formas principais de capturar dados: parâmetros de rota (req.params), query strings (req.query) e body (req.body). Parâmetros de rota são valores obrigatórios na URL; query strings são opcionais e vêm após ?. O body contém dados estruturados enviados em POST/PUT.
// Parâmetro de rota: /produtos/123
app.get('/produtos/:id', (req, res) => {
console.log(req.params.id); // "123"
res.json({ id: req.params.id });
});
// Query string: /produtos?categoria=eletrônicos&preco=100
app.get('/produtos', (req, res) => {
console.log(req.query.categoria); // "eletrônicos"
console.log(req.query.preco); // "100"
res.json({ categoria: req.query.categoria });
});
// Body (requer middleware express.json())
app.post('/produtos', (req, res) => {
console.log(req.body.nome); // dados do formulário
res.json({ received: req.body });
});
Middlewares: Processamento em Cadeia
Middlewares são funções executadas entre a requisição e a resposta. Cada middleware pode modificar req e res, passar o controle para o próximo middleware via next() ou encerrar a requisição com uma resposta. Essa arquitetura permite separar responsabilidades como autenticação, validação e logging.
const express = require('express');
const app = express();
// Middleware global
app.use(express.json()); // Parse JSON automaticamente
// Middleware customizado de logging
app.use((req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
next(); // Passa para o próximo middleware
});
// Middleware de autenticação
const verificarToken = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ erro: 'Token ausente' });
}
req.usuario = { id: 1, nome: 'João' }; // Simulado
next();
};
// Usando middleware em rota específica
app.get('/dados-privados', verificarToken, (req, res) => {
res.json({ mensagem: `Olá, ${req.usuario.nome}` });
});
// Middleware de erro (deve ser o último)
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ erro: 'Erro interno do servidor' });
});
app.listen(3000);
Estrutura de APIs REST Profissional
Organização em Camadas
Uma API REST profissional separava responsabilidades em camadas: rotas, controllers e models. As rotas apenas mapeiam URLs para funções; controllers contêm a lógica de negócio; models representam dados. Essa estrutura facilita testes, manutenção e escalabilidade.
// routes/usuarios.js
const express = require('express');
const router = express.Router();
const usuariosController = require('../controllers/usuariosController');
router.get('/', usuariosController.listar);
router.post('/', usuariosController.criar);
router.get('/:id', usuariosController.obter);
router.put('/:id', usuariosController.atualizar);
router.delete('/:id', usuariosController.deletar);
module.exports = router;
// controllers/usuariosController.js
const usuarios = []; // Simulado
exports.listar = (req, res) => {
res.json(usuarios);
};
exports.criar = (req, res) => {
const novoUsuario = { id: Date.now(), ...req.body };
usuarios.push(novoUsuario);
res.status(201).json(novoUsuario);
};
exports.obter = (req, res) => {
const usuario = usuarios.find(u => u.id == req.params.id);
if (!usuario) return res.status(404).json({ erro: 'Não encontrado' });
res.json(usuario);
};
exports.atualizar = (req, res) => {
const usuario = usuarios.find(u => u.id == req.params.id);
if (!usuario) return res.status(404).json({ erro: 'Não encontrado' });
Object.assign(usuario, req.body);
res.json(usuario);
};
exports.deletar = (req, res) => {
const index = usuarios.findIndex(u => u.id == req.params.id);
if (index === -1) return res.status(404).json({ erro: 'Não encontrado' });
usuarios.splice(index, 1);
res.status(204).send();
};
// app.js
const express = require('express');
const usuariosRouter = require('./routes/usuarios');
const app = express();
app.use(express.json());
app.use('/api/usuarios', usuariosRouter);
app.listen(3000);
Tratamento de Erros e Validação
Toda API robusta precisa validar dados recebidos e comunicar erros claramente. Use bibliotecas como joi ou express-validator para validação declarativa. Sempre retorne status HTTP apropriados: 400 para dados inválidos, 404 para não encontrado, 500 para erro do servidor.
const { body, validationResult } = require('express-validator');
app.post('/usuarios',
body('email').isEmail(),
body('idade').isInt({ min: 18 }),
(req, res) => {
const erros = validationResult(req);
if (!erros.isEmpty()) {
return res.status(400).json({ erros: erros.array() });
}
// Lógica de criação
res.status(201).json({ success: true });
}
);
Conclusão
Os três pilares do Express.js — roteamento bem organizado, middlewares para separar responsabilidades e estrutura em camadas — formam a base de qualquer API REST profissional. Domine o fluxo de requisição-resposta, use middlewares para código limpo e escalável, e organize suas rotas e controllers de forma lógica. Esses fundamentos aplicados transformam um projeto caótico em uma aplicação mantível e testável.