0
Promoção de Volta das Aulas ! Cursos com 25% OFF no menu "Cursos"
outubro 14, 2025
0
César Fontanella

Desvendando o Express: middleware em profundidade — composição, ordem e padrões (na prática)

Resumo rápido: middleware é o coração do Express. Ele recebe o req, pode enriquecer o res, decidir se responde ou chama next(). Aqui você aprende ordem de execução, tipos de middleware (de app, de roteador, de erro), padrões úteis (logger, validação, auth, rate limit) e como evitar armadilhas com assíncronos.


O que é middleware (e por que você deve se importar)

Um middleware é uma função (req, res, next) que:

  • Lê/modifica req e res;
  • Encaminha o fluxo com next() ou encerra a resposta;
  • Pode gerar erros chamando next(err).

A ordem importa: middlewares são executados na sequência em que foram registrados.


Setup mínimo

mkdir express-mw && cd express-mw
npm init -y
npm i express express-rate-limit helmet cors
npm i -D nodemon

package.json (trecho):

{
  "type": "module",
  "scripts": { "dev": "nodemon src/server.js" },
  "dependencies": {
    "cors": "^2.8.5",
    "express": "^4.19.0",
    "express-rate-limit": "^7.0.0",
    "helmet": "^7.0.0"
  }
}

Estrutura:

src/
  server.js
  app.js
  middlewares/
    logger.js
    validate.js
    auth.js
  routes/
    index.js
    users.js

Tipos de middleware que você usará no dia a dia

1) De aplicação (global)

Aplicado ao app inteiro, antes das rotas.

// src/app.js
import express from "express";
import helmet from "helmet";
import cors from "cors";
import rateLimit from "express-rate-limit";
import logger from "./middlewares/logger.js";
import indexRouter from "./routes/index.js";
import usersRouter from "./routes/users.js";

const app = express();

// Segurança e hardening
app.use(helmet());
// CORS (ajuste origin conforme seu front)
app.use(cors());
// Limite de requisições (protege sua API)
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));

// Parsers
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Logger custom
app.use(logger);

// Rotas
app.use("/", indexRouter);
app.use("/users", usersRouter);

// 404
app.use((req, res) => res.status(404).json({ error: "Rota não encontrada" }));

// Handler central de erros
app.use((err, req, res, next) => {
  console.error("[ERRO]", err);
  res.status(err.status || 500).json({ error: err.message || "Erro interno" });
});

export default app;

2) De roteador (escopo menor)

Limitado a um conjunto de rotas — ótimo para regras específicas.

// src/routes/users.js
import { Router } from "express";
import { requireAuth } from "../middlewares/auth.js";
import { validateBody } from "../middlewares/validate.js";

const router = Router();

// Exige auth e validação só neste grupo
router.use(requireAuth);

router.get("/", (req, res) => {
  res.json([{ id: 1, name: "Ada" }]);
});

router.post(
  "/",
  validateBody({ required: ["name"] }),
  (req, res) => res.status(201).json({ id: 2, name: req.body.name })
);

export default router;

3) De erro (quatro parâmetros)

Detecta falhas e padroniza a resposta.

// Assinatura: (err, req, res, next)
// Já incluímos um handler central no app; você pode ter handlers especializados por domínio se necessário.

Padrões de middleware prontos para usar

Logger de requests

// src/middlewares/logger.js
export default function logger(req, res, next) {
  const t0 = Date.now();
  res.on("finish", () => {
    const ms = Date.now() - t0;
    console.log(`${req.method} ${req.originalUrl} → ${res.statusCode} (${ms}ms)`);
  });
  next();
}

Validação de corpo enxuta

// src/middlewares/validate.js
export function validateBody({ required = [] } = {}) {
  return (req, res, next) => {
    const missing = required.filter((k) => !(k in req.body));
    if (missing.length) return next({ status: 400, message: `Campos obrigatórios: ${missing.join(", ")}` });
    next();
  };
}

Autenticação simples (mock)

// src/middlewares/auth.js
export function requireAuth(req, res, next) {
  const token = req.get("Authorization")?.replace("Bearer ", "");
  if (!token || token !== "seu_token_aqui") {
    return next({ status: 401, message: "Não autorizado" });
  }
  // Ex.: anexar usuário ao req
  req.user = { id: "123", role: "admin" };
  next();
}

Armadilhas com assíncronos (e como evitar)

Problema comum: lançar erro dentro de async sem capturar — a execução “some” e o handler central não recebe o erro.

Solução 1 — try/catch + next(err):

router.get("/:id", async (req, res, next) => {
  try {
    const user = await buscarUsuario(req.params.id); // I/O
    if (!user) return next({ status: 404, message: "Usuário não encontrado" });
    res.json(user);
  } catch (err) {
    next(err);
  }
});

Solução 2 — wrapper utilitário:

const wrap = (fn) => (req, res, next) => Promise.resolve(fn(req, res, next)).catch(next);

router.get("/:id", wrap(async (req, res) => {
  const user = await buscarUsuario(req.params.id);
  if (!user) throw { status: 404, message: "Usuário não encontrado" };
  res.json(user);
}));

Ordem, escopo e composição: três regras de ouro

  1. Registre parsers e segurança primeiro, antes das rotas que dependem deles.
  2. Aplique middlewares no menor escopo possível (de roteador) para manter performance e clareza.
  3. Padronize erros: tudo que falhar cai no mesmo handler de erro.

FAQ rápido

1) Devo usar um middleware “global” para tudo?
Não. Prefira escopo de roteador para regras específicas, mantendo o global só para cross-cutting (segurança, parsers, log).

2) Como retornar erros customizados?
Crie objetos { status, message } e passe a next(err); o handler central converte em resposta JSON.

3) Como lidar com limites e segurança?
Combine helmet, cors (origem controlada) e express-rate-limit. Para auth, use tokens (JWT, por exemplo) e renove chaves com regularidade.