Node.js e Express¶
Node.js executa JavaScript fora do navegador (motor V8) e Express é o framework web mais popular sobre ele. Juntos com MongoDB (banco de documentos, em NoSQL) e um front-end (Angular, na sigla original) formam a MEAN Stack: MongoDB + Express + Angular + Node.js. A grande vantagem: uma só linguagem (JavaScript/JSON) em todas as camadas, do banco ao navegador, o que acelera protótipos e simplifica a equipe.
flowchart LR
B[Navegador<br/>Angular SPA] -- "HTTP / JSON (REST)" --> E[Express<br/>Node.js]
E -- "Mongoose (ODM)" --> M[(MongoDB)]
E --> A[Passport<br/>autenticação]
Definição: Node.js, npm e package.json
Node.js é um ambiente de execução de JavaScript orientado a eventos e a I/O assíncrono não bloqueante: uma só thread
atende muitas requisições porque, ao esperar o banco ou a rede, registra uma função de retorno e segue adiante. npm é o gerenciador
de pacotes; o package.json descreve o projeto e suas dependências (com versão), e a pasta node_modules (não versionada) é
recriada com npm install. npm init cria o arquivo; npm install pacote --save (hoje, por padrão) registra a dependência.
Express: servidor, middlewares e rotas¶
Um servidor "puro" do Node responde a tudo na mesma função. O Express acrescenta middlewares, rotas, visões e utilidades.
Definição: middleware
Função que recebe a requisição (req), a resposta (res) e a próxima etapa (next) e pode ler/alterar a requisição, responder ou
passar adiante. Uma pilha de middlewares é aplicada a cada requisição (segurança, log, parsing do corpo, arquivos estáticos,
autenticação, tratamento de erro). A ordem de registro importa. A partir do Express 4 cada middleware é um módulo à parte.
const express = require("express");
const app = express();
app.use(express.json()); // lê o corpo JSON
app.use(express.static("public")); // arquivos estáticos
app.get("/contatos/:id", (req, res) => { // rota GET com parâmetro
const contato = contatos.find(c => c._id === req.params.id);
if (!contato) return res.status(404).json({ erro: "Contato não encontrado" });
res.json(contato);
});
app.listen(3000);
Elementos do Express:
- Variáveis de ambiente/configuração (
app.set,process.env): porta, view engine, conexão com o banco. Não coloque segredos no código. - Rotas:
app.get/post/put/delete(caminho, ...handlers)ligadas aos verbos HTTP;req.params(caminho),req.query(query string),req.body. - Template engines (EJS, Pug, Handlebars): geram HTML no servidor com
res.render("view", dados). Em uma SPA, o servidor entrega só JSON. - Organização em MVC: routes (mapeiam URLs), controllers (funções que tratam a requisição, a ponte entre a view e os dados),
models (acesso ao banco). Módulos como
express-loadcarregavam essas pastas automaticamente; hoje se usamrequire/importexplícitos. - Desenvolvimento:
nodemonreinicia o servidor a cada alteração.
API REST¶
Com o Express é simples expor recursos como endpoints REST (REST): GET /contatos (lista),
GET /contatos/:id, POST /contatos (cria), PUT /contatos/:id (atualiza), DELETE /contatos/:id, retornando JSON e códigos HTTP corretos
(200, 201, 204, 400, 401, 404, 500).
Programação assíncrona: de callbacks a async/await¶
Operações de rede e banco são assíncronas. A evolução dos estilos:
| Estilo | Problema/vantagem |
|---|---|
| Callbacks | Funções aninhadas formam a pirâmide da perdição (callback hell), e try/catch não captura erros assíncronos |
Promises (.then().catch()) |
Valor futuro encadeável, erros propagados pela cadeia |
async/await |
Código assíncrono com aparência sequencial e try/catch normal — o padrão atual |
app.get("/contatos", async (req, res, next) => {
try {
const contatos = await Contato.find().exec();
res.json(contatos);
} catch (e) { next(e); } // middleware de erro trata
});
Mongoose: esquemas para um banco sem esquema¶
O MongoDB não força esquema — a integridade dos dados passa a ser responsabilidade da aplicação. O Mongoose é um ODM
(Object-Document Mapper): você define esquemas (campos, tipos, obrigatoriedade, valores padrão, validações), cria modelos a partir deles e
usa métodos como find, findById, create, findByIdAndUpdate, deleteOne. Gerencia também a conexão (eventos connected, error,
disconnected; uma conexão reaproveitada por toda a aplicação) e referências entre documentos (ref + populate, como um JOIN na aplicação).
Aproxima-se do padrão Active Record.
const esquema = new mongoose.Schema({
nome: { type: String, required: true },
email: { type: String, required: true, unique: true, index: true },
emergencia: { type: mongoose.Schema.Types.ObjectId, ref: "Contato" }
});
const Contato = mongoose.model("Contato", esquema);
Atualização por substituição de documento
Aceitar o objeto inteiro enviado pelo cliente e substituir o documento permite que ele altere campos que não deveria (o equivalente do mass assignment;
veja Segurança). Atualize só os campos permitidos ($set).
Autenticação com Passport e OAuth 2.0¶
Passport é um middleware de autenticação com estratégias plugáveis (usuário e senha, OAuth 2.0 com GitHub/Google, JWT). Fluxo típico:
registrar a aplicação no provedor, configurar a estratégia (ID e segredo do cliente em variáveis de ambiente), serializar o usuário na sessão e
desserializar a cada requisição, e então proteger as rotas com um middleware que exige usuário autenticado (resposta 401 para APIs). No
front-end, um interceptador de requisições redireciona ao login quando recebe 401. Teoria do protocolo em Segurança.
Segurança em aplicações Express¶
Veja o panorama em Segurança em aplicações web. Específico do ecossistema:
- Helmet: conjunto de middlewares que ajustam cabeçalhos HTTP: escondem o
X-Powered-By(que revela a tecnologia),X-Frame-Options(contra clickjacking),X-Content-Type-Options: nosniff,Strict-Transport-Security, Content-Security-Policy (o antigoX-XSS-Protectionfoi abandonado).app.use(helmet()). - Injeção de operadores no MongoDB (NoSQL injection): o MongoDB é imune à injeção de SQL, mas não a objetos maliciosos. Se a API espera um
texto (
id) e recebe um objeto como{ "$ne": null }, uma consulta ou remoção pode atingir todos os documentos. Valide e converta os tipos, e sanitize chaves que comecem com$(express-mongo-sanitize, validação com Joi/Zod). - Mensagens de erro genéricas, limite de tamanho do corpo, limitação de taxa (
express-rate-limit), CORS configurado e HTTPS.
Ferramentas de build, testes e entrega¶
| Tema | No livro (2015) | Hoje |
|---|---|---|
| Dependências do front-end | Bower | npm/pnpm + bundlers (Vite, esbuild, webpack); Bower foi abandonado |
| Automação (minificar e concatenar) | Grunt (usemin) |
Vite, webpack, npm scripts; Gulp/Grunt são legados |
| Testes de unidade | Karma + Jasmine | Jest ou Vitest; testes de API com Supertest |
| Testes de ponta a ponta | Protractor (descontinuado) + Page Objects | Playwright ou Cypress, mantendo o padrão Page Object (Qualidade) |
| Integração contínua | Travis CI | GitHub Actions, GitLab CI (CI/CD) |
| Deploy | PaaS (OpenShift) a cada commit bem-sucedido | PaaS (Render, Fly.io, Heroku), contêineres/Kubernetes, funções serverless (Nuvem) |
A ideia que continua válida: tudo que é manual está sujeito a erro, então cada commit dispara build, testes e (se passar) implantação automática.
Os mocks do back-end no front-end ($httpBackend no Angular) correspondem hoje a ferramentas como MSW ou HttpClientTestingModule.
Quando usar (e quando não)¶
Bom para: APIs e BFFs I/O-bound (muitas conexões, pouco processamento), tempo real (WebSocket), protótipos e times de JavaScript/TypeScript. Cuidado com tarefas intensivas de CPU (bloqueiam o event loop; use worker threads ou serviços à parte). Hoje prefira TypeScript e, para estruturar projetos grandes, frameworks como NestJS (inspirado no Angular) ou Fastify. O mesmo conceito de stack com outras tecnologias: Spring.