Criando APIs robustas com Node.js e Express
Uma API pronta para produção precisa ser previsível sob alta carga, segura contra ataques comuns e fácil de manter ao longo do tempo.
Embora o Express seja um microframework minimalista, adotar convenções rígidas de validação de dados, tratamento de erros e segurança HTTP transforma uma aplicação básica em um serviço backend robusto e escalável.
1. Validação estrita de entrada com Zod
Nunca confie em dados enviados pelo cliente. A melhor abordagem é validar os payloads logo na camada de entrada antes que atinjam as regras de negócio:
import type { Request, Response, NextFunction } from "express";
import { z, type AnyZodObject } from "zod";
// Middleware genérico para validação de requisições
export const validate = (schema: AnyZodObject) => {
return async (req: Request, res: Response, next: NextFunction) => {
const result = await schema.safeParseAsync({
body: req.body,
query: req.query,
params: req.params,
});
if (!result.success) {
return res.status(400).json({
error: "Validation failed",
details: result.error.flatten().fieldErrors,
});
}
req.body = result.data.body;
return next();
};
};
// Schema declarativo de exemplo
export const CreateUserSchema = z.object({
body: z.object({
name: z.string().min(2, "Nome deve ter pelo menos 2 caracteres"),
email: z.string().email("Formato de e-mail inválido"),
role: z.enum(["admin", "user"]).default("user"),
}),
});
2. Tratamento centralizado de erros
Erros não capturados podem derrubar o processo do Node.js. Crie um middleware de erro centralizado ao final da cadeia de rotas para padronizar as respostas HTTP e evitar o vazamento de stack traces em ambiente de produção:
import type { Request, Response, NextFunction } from "express";
export class AppError extends Error {
constructor(
public readonly statusCode: number,
public readonly message: string,
public readonly details?: unknown
) {
super(message);
Object.setPrototypeOf(this, new.target.prototype);
}
}
export function errorHandler(
err: unknown,
_req: Request,
res: Response,
_next: NextFunction
) {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
status: "error",
message: err.message,
...(err.details ? { details: err.details } : {}),
});
}
// Log estruturado do erro interno
console.error("[InternalError]:", err);
return res.status(500).json({
status: "error",
message: process.env.NODE_ENV === "production" ? "Erro interno do servidor" : String(err),
});
}
3. Checklist de Segurança e Produção
- Cabeçalhos de segurança: Utilize
helmetpara configurar cabeçalhos defensivos comoContent-Security-Policy,X-Content-Type-Optionse remoção doX-Powered-By. - Rate Limiting: Proteja endpoints de autenticação e rotas públicas contra ataques de força bruta com
express-rate-limit. - CORS configurado de forma explícita: Nunca utilize
origin: "*"em ambientes onde autenticação baseada em cookies ou tokens confidenciais é necessária. - Arquitetura modular em camadas:
modules/<feature>/<feature>.routes.ts: Definição de rotas e middlewares.modules/<feature>/<feature>.controller.ts: Orquestração de requisição/resposta HTTP.modules/<feature>/<feature>.service.ts: Lógica de negócio pura e desacoplada do Express.modules/<feature>/<feature>.schema.ts: Schemas Zod para tipagem e validação.
Isolando cada responsabilidade, sua API se torna testável, previsível e pronta para escalar com consistência.