Seu carrinho está vazio no momento!

Introdução (3 min)
Caros futuros mestres das APIs, sejam bem-vindos à Aula 51! Preparem-se para desvendar um dos pilares mais significativos no desenvolvimento de serviços web robustos: a validação de dados. Imaginem que sua API é um ponto de entrada para um sistema altamente complexo, como uma alfândega em um país rigoroso. Não se pode simplesmente permitir que qualquer pessoa entre com qualquer tipo de bagagem, não é mesmo? É crucial verificar a identidade, o propósito da visita e o conteúdo da bagagem para garantir a segurança e a ordem.
Essa é a essência da validação de dados em APIs. Ela é o porteiro digital que examina minuciosamente todas as informações que chegam à sua aplicação. Sem uma verificação adequada, sua API estaria vulnerável a dados maliciosos, inconsistentes ou simplesmente errados, o que pode levar a falhas críticas, brechas de segurança, corrupção de informações no banco de dados e uma experiência de usuário (UX) terrível. É um componente fundamental para a integridade, confiabilidade e segurança de qualquer sistema moderno.
Nesta aula, nosso objetivo é capacitá-los com ferramentas e conhecimentos para implementar uma validação de entrada de dados robusta no seu ecossistema Node.js/Express. Vamos explorar três bibliotecas de ponta – Joi, Yup e express-validator – cada uma com suas particularidades, permitindo que vocês escolham a mais adequada para cada cenário. Ao final, terão a capacidade de aplicar esquemas de validação complexos, tratar erros de forma elegante e construir APIs que resistem ao teste do tempo e dos dados mal-intencionados.
No contexto Node.js e Express, a validação de dados se integra perfeitamente como um middleware, um passo intermediário entre a chegada da requisição e o processamento final pelo seu controlador (handler). Isso possibilita interceptar e verificar os dados antes mesmo que eles cheguem à lógica de negócio, garantindo que apenas informações válidas avancem.
Conceito Fundamental (7 min)
A validação de dados é o processo de assegurar que a entrada fornecida por um usuário ou outro sistema externo atenda a um conjunto de regras predefinidas antes de ser processada ou armazenada. Em uma API RESTful, essa entrada geralmente se manifesta no corpo (body) da requisição, nos parâmetros (params) da URL ou nas consultas (query params). O objetivo primordial é manter a integridade e a segurança dos dados do seu sistema.
A terminologia correta da indústria nos aponta para conceitos como:
- Schema (Esquema): Um modelo ou blueprint que define a estrutura, os tipos de dados e as restrições esperadas para um conjunto de dados. Por exemplo, um esquema de usuário pode especificar que o campo
emaildeve ser uma string em formato de email válido e o camposenhadeve ter no mínimo 8 caracteres. - Payload: Refere-se aos dados enviados no corpo de uma requisição HTTP, que são o principal alvo da validação.
- Middleware: Funções que têm acesso ao objeto de requisição (
req), ao objeto de resposta (res) e à próxima função middleware no ciclo de requisição-resposta de um aplicativo Express. É o local ideal para executar a validação. - Sanitização (Sanitization): O processo de limpar ou filtrar dados de entrada para remover caracteres indesejados ou potencialmente perigosos, como tags HTML (prevenção de XSS – Cross-Site Scripting) ou espaços em branco excessivos.
- Escaping: A técnica de modificar caracteres especiais em uma string para que eles sejam interpretados literalmente e não como parte de um comando ou sintaxe, útil para prevenir ataques de injeção.
Casos de uso reais em produção são abundantes e demonstram a relevância da validação:
- Registro de Usuário: Garantir que o email seja único e válido, a senha atenda aos requisitos de complexidade, o nome não seja vazio, e assim por diante.
- Criação/Atualização de Produtos: Verificar se o preço é um número positivo, o nome do produto tem um comprimento razoável, a descrição não contém caracteres proibidos.
- Filtros de Busca: Validar se os parâmetros de ordenação ou paginação são números inteiros esperados para evitar erros no banco de dados.
- Upload de Arquivos: Embora complexo, a validação inicial pode verificar tipo de arquivo, tamanho máximo, etc.
A validação de dados se integra de forma sinérgica com diversas tecnologias:
- Com a validação front-end, ela atua como uma segunda linha de defesa. Embora o front-end possa oferecer feedback imediato ao usuário, a validação no back-end é a única que pode ser considerada autoritativa e segura, pois o front-end pode ser facilmente manipulado.
- Com bancos de dados, a validação de dados no back-end previne a inserção de informações inconsistentes ou inválidas, complementando as restrições de esquema que podem existir no próprio banco (como
NOT NULL,UNIQUE, tipos de coluna). - Com testes automatizados, a validação robusta simplifica a escrita de testes, pois a lógica de negócio pode assumir que os dados de entrada já estão em um formato aceitável.
As vantagens de uma validação de dados rigorosa são notáveis:
- Robustez: O sistema se torna mais resistente a entradas inesperadas, reduzindo a chance de crashes e comportamentos imprevisíveis.
- Segurança: Minimiza o risco de ataques como injeção de SQL (SQL Injection), Cross-Site Scripting (XSS) e outros, ao impedir que dados maliciosos cheguem aos componentes sensíveis.
- Integridade dos Dados: Garante que apenas dados válidos e bem-estruturados sejam armazenados, mantendo a consistência do seu banco de dados.
- Melhor Experiência do Usuário (UX): Fornece mensagens de erro claras e informativas, orientando o usuário sobre como corrigir suas entradas.
- Manutenibilidade do Código: Simplifica a lógica de negócio, pois ela não precisa se preocupar em lidar com dados inválidos, tornando o código mais limpo e fácil de manter.
Contudo, existem algumas desvantagens a considerar:
- Overhead de Desempenho: Embora geralmente mínimo, o processo de validação adiciona uma pequena sobrecarga de processamento a cada requisição.
- Curva de Aprendizado: Ferramentas mais poderosas podem exigir um tempo inicial para domínio da sintaxe e dos conceitos.
- Verboso em Demasia: Se não for bem organizada, a lógica de validação pode tornar-se repetitiva e sobrecarregar os arquivos de rota.
Implementação Prática (10 min)
Vamos agora colocar a mão na massa e desenvolver um exemplo prático que demonstre a validação de dados utilizando as bibliotecas express-validator, Joi e Yup. Nosso cenário será um simples gerenciamento de usuários, com um endpoint para criar um novo usuário.
Primeiro, crie um novo diretório para o seu projeto e inicialize-o:
mkdir validacao-api
cd validacao-api
npm init -y
Agora, instale as dependências essenciais:
npm install express express-validator joi yup
Vamos criar a estrutura básica do nosso projeto:
validacao-api/
├── package.json
├── index.js
├── routes/
│ └── usuarioRoutes.js
├── middlewares/
│ └── validationMiddleware.js
└── schemas/
└── usuarioSchema.js
Primeiro, configure o arquivo index.js, que será o ponto de entrada da nossa aplicação Express:
// index.js
const express = require('express'); // Importa o módulo 'express' para criar o servidor web
const usuarioRoutes = require('./routes/usuarioRoutes'); // Importa as rotas de usuário que vamos criar
const app = express(); // Inicializa o aplicativo Express
const PORT = process.env.PORT || 3000; // Define a porta, usando a variável de ambiente ou 3000 por padrão
// Middleware para fazer o parsing do corpo da requisição em formato JSON
app.use(express.json());
// Monta as rotas de usuário no caminho '/api/usuarios'
app.use('/api/usuarios', usuarioRoutes);
// Rota de teste simples para verificar se a API está funcionando
app.get('/', (req, res) => {
res.send('API de validação de dados funcionando!');
});
// Middleware genérico para tratamento de erros (opcional, mas uma boa prática)
app.use((err, req, res, next) => {
console.error("Erro inesperado:", err.stack); // Registra o erro no console
res.status(500).json({ status: 'erro', mensagem: 'Ocorreu um erro interno no servidor.' }); // Retorna um erro 500
});
// Inicia o servidor e escuta na porta especificada
app.listen(PORT, () => {
console.log(Servidor rodando na porta ${PORT});
console.log('Para testar, use ferramentas como Postman, Insomnia ou cURL.');
});
1. Validação com express-validator
Vamos começar com express-validator, que se integra de forma muito natural com o fluxo de middlewares do Express.
Crie o arquivo routes/usuarioRoutes.js:
// routes/usuarioRoutes.js
const express = require('express');
const { body, validationResult } = require('express-validator'); // Importa 'body' para validar o corpo da requisição e 'validationResult' para coletar os erros
const router = express.Router(); // Cria um novo objeto Router do Express
// Simulação de um banco de dados de usuários
let usuarios = [];
let nextId = 1;
/* Middleware para lidar com os resultados da validação do express-validator.
Se houver erros, ele formata e envia uma resposta 400.
/
const handleValidationErrors = (req, res, next) => {
const errors = validationResult(req); // Coleta todos os erros de validação da requisição
if (!errors.isEmpty()) { // Se a lista de erros não estiver vazia
// Loga os erros para depuração, é uma boa prática para identificar problemas
console.error("Erros de validação (express-validator):", errors.array());
// Retorna uma resposta HTTP 400 (Bad Request) com os erros formatados
return res.status(400).json({
status: 'erro',
mensagem: 'Dados inválidos na requisição.',
erros: errors.array() // Envia o array de erros para o cliente
});
}
next(); // Se não houver erros, passa para o próximo middleware ou para o handler da rota
};
// Rota POST para criar um novo usuário com validação express-validator
router.post(
'/', // Caminho da rota
[
// Middleware de validação para o campo 'nome'
body('nome')
.trim() // Remove espaços em branco do início e fim
.notEmpty().withMessage('O nome é um campo obrigatório.') // Garante que não está vazio
.isLength({ min: 3, max: 100 }).withMessage('O nome deve ter entre 3 e 100 caracteres.'), // Verifica o comprimento
// Middleware de validação para o campo 'email'
body('email')
.trim()
.notEmpty().withMessage('O email é um campo obrigatório.')
.isEmail().withMessage('Formato de email inválido.') // Verifica se é um email válido
.normalizeEmail(), // Normaliza o email (ex: minusculo)
// Middleware de validação para o campo 'senha'
body('senha')
.notEmpty().withMessage('A senha é um campo obrigatório.')
.isLength({ min: 6 }).withMessage('A senha deve ter no mínimo 6 caracteres.'), // Requisito de comprimento
handleValidationErrors // Nosso middleware customizado para lidar com os erros
],
// Handler da rota (executado apenas se a validação passar)
(req, res) => {
const { nome, email, senha } = req.body; // Pega os dados validados do corpo da requisição
// Cria o novo usuário com um ID único
const novoUsuario = { id: nextId++, nome, email, senha };
usuarios.push(novoUsuario); // Adiciona ao nosso "banco de dados"
// Retorna uma resposta HTTP 201 (Created) com o usuário criado
res.status(201).json({
status: 'sucesso',
mensagem: 'Usuário criado com sucesso!',
usuario: { id: novoUsuario.id, nome: novoUsuario.nome, email: novoUsuario.email } // Não retorna a senha por segurança
});
}
);
// Rota GET para listar todos os usuários (para verificação)
router.get('/', (req, res) => {
res.status(200).json({ status: 'sucesso', usuarios: usuarios.map(u => ({ id: u.id, nome: u.nome, email: u.email })) });
});
module.exports = router; // Exporta o router para ser usado em index.js
Para testar (com cURL):
Cenário 1: Sucesso
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Alice Silva", "email": "[email protected]", "senha": "senha123"}' http://localhost:3000/api/usuarios
Cenário 2: Erros de validação (email inválido, senha curta)
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Bob", "email": "bob@", "senha": "123"}' http://localhost:3000/api/usuarios
2. Validação com Joi
Joi é uma biblioteca de validação de esquema muito poderosa e flexível. Para integrá-la ao Express, geralmente criamos um middleware genérico.
Crie o arquivo schemas/usuarioSchema.js para definir o esquema Joi:
// schemas/usuarioSchema.js
const Joi = require('joi'); // Importa a biblioteca Joi
// Define o esquema de validação para um usuário
const criarUsuarioSchemaJoi = Joi.object({
// O campo 'nome' deve ser uma string, ser obrigatório e ter entre 3 e 100 caracteres
nome: Joi.string().trim().min(3).max(100).required().messages({
'string.base': 'O nome deve ser uma string.',
'string.empty': 'O nome é um campo obrigatório.',
'string.min': 'O nome deve ter no mínimo {#limit} caracteres.',
'string.max': 'O nome deve ter no máximo {#limit} caracteres.',
'any.required': 'O nome é um campo obrigatório.'
}),
// O campo 'email' deve ser uma string em formato de email válido e ser obrigatório
email: Joi.string().trim().email().required().messages({
'string.base': 'O email deve ser uma string.',
'string.empty': 'O email é um campo obrigatório.',
'string.email': 'O formato do email é inválido.',
'any.required': 'O email é um campo obrigatório.'
}),
// O campo 'senha' deve ser uma string, ser obrigatório e ter no mínimo 6 caracteres
senha: Joi.string().min(6).required().messages({
'string.base': 'A senha deve ser uma string.',
'string.empty': 'A senha é um campo obrigatório.',
'string.min': 'A senha deve ter no mínimo {#limit} caracteres.',
'any.required': 'A senha é um campo obrigatório.'
})
});
module.exports = {
criarUsuarioSchemaJoi // Exporta o esquema para ser usado em outros arquivos
};
Crie o arquivo middlewares/validationMiddleware.js para o middleware genérico de validação:
// middlewares/validationMiddleware.js
// Este middleware genérico pode ser usado para Joi ou Yup
const validate = (schema) => async (req, res, next) => {
try {
// Valida o corpo da requisição contra o esquema fornecido
// { abortEarly: false } garante que todos os erros sejam coletados, não apenas o primeiro
const validatedBody = await schema.validateAsync(req.body, { abortEarly: false });
req.body = validatedBody; // Opcional: substitui o corpo da requisição pelo objeto validado/limpo
next(); // Se a validação for bem-sucedida, passa para o próximo middleware/handler
} catch (error) {
// Loga o erro completo para depuração. Em produção, você pode querer um logger mais robusto.
console.error("Erro de validação (Joi/Yup):", error.details);
// Formata os erros para um array mais amigável, similar ao express-validator
const errosFormatados = error.details.map(detail => ({
campo: detail.context.key, // Nome do campo com erro
mensagem: detail.message.replace(/['"]/g, ''), // Mensagem de erro, removendo aspas
valor_recebido: detail.context.value // Valor que causou o erro
}));
// Retorna uma resposta HTTP 400 (Bad Request) com os erros
return res.status(400).json({
status: 'erro',
mensagem: 'Dados inválidos na requisição.',
erros: errosFormatados
});
}
};
module.exports = validate; // Exporta a função de middleware
Agora, modifique routes/usuarioRoutes.js para usar o Joi. Você pode comentar a rota anterior ou criar uma nova rota para Joi. Vou adicionar uma nova para demonstrar ambos.
// routes/usuarioRoutes.js (CONTINUAÇÃO)
// ... imports existentes ...
const validate = require('../middlewares/validationMiddleware'); // Importa o middleware genérico
const { criarUsuarioSchemaJoi } = require('../schemas/usuarioSchema'); // Importa o esquema Joi
// ... handleValidationErrors ...
// Rota POST para criar um novo usuário com validação Joi
router.post(
'/joi', // Novo caminho da rota para demonstrar Joi
validate(criarUsuarioSchemaJoi), // Usa o middleware genérico com o esquema Joi
// Handler da rota (executado apenas se a validação passar)
(req, res) => {
const { nome, email, senha } = req.body; // Pega os dados validados do corpo da requisição
// Cria o novo usuário com um ID único
const novoUsuario = { id: nextId++, nome, email, senha };
usuarios.push(novoUsuario); // Adiciona ao nosso "banco de dados"
// Retorna uma resposta HTTP 201 (Created) com o usuário criado
res.status(201).json({
status: 'sucesso',
mensagem: 'Usuário criado com sucesso com Joi!',
usuario: { id: novoUsuario.id, nome: novoUsuario.nome, email: novoUsuario.email }
});
}
);
// ... router.get ...
Para testar (com cURL):
Cenário 1: Sucesso (Joi)
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Carlos Pereira", "email": "[email protected]", "senha": "senhasecreta"}' http://localhost:3000/api/usuarios/joi
Cenário 2: Erros de validação (Joi)
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Ca", "email": "carlosexample.com", "senha": "abc"}' http://localhost:3000/api/usuarios/joi
3. Validação com Yup
Yup é outra biblioteca de validação baseada em esquemas, conhecida por sua sintaxe fluente e intuitiva. A integração com o Express é muito similar à do Joi, usando o mesmo middleware genérico.
Atualize schemas/usuarioSchema.js para incluir o esquema Yup:
// schemas/usuarioSchema.js (CONTINUAÇÃO)
// ... import Joi ...
const Yup = require('yup'); // Importa a biblioteca Yup
// Define o esquema de validação para um usuário usando Yup
const criarUsuarioSchemaYup = Yup.object().shape({
// O campo 'nome' deve ser uma string, ser obrigatório e ter entre 3 e 100 caracteres
nome: Yup.string()
.trim() // Remove espaços em branco
.min(3, 'O nome deve ter no mínimo 3 caracteres.') // Mensagem personalizada para min
.max(100, 'O nome deve ter no máximo 100 caracteres.') // Mensagem personalizada para max
.required('O nome é um campo obrigatório.'), // Mensagem personalizada para required
// O campo 'email' deve ser uma string em formato de email válido e ser obrigatório
email: Yup.string()
.trim()
.email('Formato de email inválido.') // Mensagem personalizada para email
.required('O email é um campo obrigatório.'),
// O campo 'senha' deve ser uma string, ser obrigatório e ter no mínimo 6 caracteres
senha: Yup.string()
.min(6, 'A senha deve ter no mínimo 6 caracteres.')
.required('A senha é um campo obrigatório.')
});
module.exports = {
criarUsuarioSchemaJoi,
criarUsuarioSchemaYup // Exporta o esquema Yup também
};
Modifique routes/usuarioRoutes.js para incluir a rota com Yup:
// routes/usuarioRoutes.js (CONTINUAÇÃO)
// ... imports existentes ...
const { criarUsuarioSchemaYup } = require('../schemas/usuarioSchema'); // Importa o esquema Yup
// ... handleValidationErrors ...
// Rota POST para criar um novo usuário com validação Yup
router.post(
'/yup', // Novo caminho da rota para demonstrar Yup
validate(criarUsuarioSchemaYup), // Usa o middleware genérico com o esquema Yup
// Handler da rota (executado apenas se a validação passar)
(req, res) => {
const { nome, email, senha } = req.body; // Pega os dados validados do corpo da requisição
// Cria o novo usuário com um ID único
const novoUsuario = { id: nextId++, nome, email, senha };
usuarios.push(novoUsuario); // Adiciona ao nosso "banco de dados"
// Retorna uma resposta HTTP 201 (Created) com o usuário criado
res.status(201).json({
status: 'sucesso',
mensagem: 'Usuário criado com sucesso com Yup!',
usuario: { id: novoUsuario.id, nome: novoUsuario.nome, email: novoUsuario.email }
});
}
);
// ... router.get ...
Para testar (com cURL):
Cenário 1: Sucesso (Yup)
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Diana Souza", "email": "[email protected]", "senha": "passwordforte"}' http://localhost:3000/api/usuarios/yup
Cenário 2: Erros de validação (Yup)
curl -X POST -H "Content-Type: application/json" -d '{"nome": "Di", "email": "dianamail.net", "senha": "abc"}' http://localhost:3000/api/usuarios/yup
Melhores Práticas Enterprise e Configurações HostGator Plano M
- Separação de Preocupações: Como demonstrado, os esquemas de validação (
schemas/usuarioSchema.js) e o middleware de validação (middlewares/validationMiddleware.js) são separados da lógica das rotas. Isso otimiza a organização e a reusabilidade. - Middleware de Validação Genérico: O
validationMiddleware.jsé um padrão enterprise. Ele permite que você utilize qualquer biblioteca de validação baseada em esquema (Joi, Yup) de forma intercambiável, apenas passando o esquema correto. - Tratamento de Erros Padronizado: As respostas de erro
400 Bad Requestcom um corpo JSON que contémstatus: 'erro'e um arrayerrossão um padrão amplamente aceito. Isso facilita para os clientes da API entenderem e processarem os erros. - Sanitização:
express-validatoreJoi/Yup(comtrim()) oferecem métodos para sanitizar os dados (como remover espaços em branco). Em cenários mais complexos, considere bibliotecas comovalidator.jspara sanitizações mais profundas ou escapar HTML. - Logging Profissional: Embora tenhamos usado
console.errorpara fins didáticos, em um ambiente de produção enterprise, seria vital integrar bibliotecas de logging comoWinstonouPino. Elas fornecem logs estruturados, níveis de severidade e a capacidade de direcionar logs para diferentes destinos (arquivos, serviços de nuvem). - Configurações HostGator Plano M:
- O código acima é 100% compatível. O HostGator Plano M oferece suporte a aplicações Node.js.
- Para deploy, certifique-se de que seu
package.jsonesteja correto e liste todas as dependências. O HostGator executaránpm installautomaticamente. - Use
process.env.PORTpara definir a porta da sua aplicação. O ambiente de hospedagem injetará a porta correta. - A estrutura de diretórios apresentada é portátil e funcional em qualquer ambiente Node.js.
Exercício Hands-On (5 min)
Desafio Prático
Sua tarefa é desenvolver um novo endpoint na API para atualizar um produto existente.
Crie uma rota PUT /api/produtos/:id que receberá um ID do produto nos parâmetros da URL e os dados do produto (nome e preço) no corpo da requisição.
Requisitos de Validação:
- O
iddo produto na URL deve ser um número inteiro e positivo. - O
nomedo produto no corpo da requisição deve ser uma string, não pode ser vazio e ter entre 3 e 100 caracteres. - O
precodo produto no corpo da requisição deve ser um número e ser maior que zero.
Escolha uma das bibliotecas que vimos (Joi, Yup ou express-validator) para implementar a validação.
Solução Detalhada Passo a Passo
Vamos usar o Joi para a solução.
- Crie um “banco de dados” de produtos na raiz do seu
routes/usuarioRoutes.js(ou crie um novoroutes/produtoRoutes.jse adicione emindex.js). Por simplicidade, adicionaremos nousuarioRoutes.jspara não alterar muito oindex.js.
// routes/usuarioRoutes.js (ADICIONE NO INÍCIO, ANTES DAS ROTAS)
// ... imports existentes ...
// Simulação de banco de dados para usuários (já existia)
let usuarios = [];
let nextId = 1;
// NOVO: Simulação de banco de dados para produtos
let produtos = [
{ id: 101, nome: "Notebook Gamer", preco: 7500.00 },
{ id: 102, nome: "Mouse Óptico", preco: 150.00 }
];
let nextProdutoId = 103;
// ... handleValidationErrors ...
- Crie um esquema de validação para produtos em
schemas/usuarioSchema.js(ou crieschemas/produtoSchema.js).
// schemas/usuarioSchema.js (ADICIONE NO FINAL)
// ... esquemas de usuário existentes ...
const atualizarProdutoSchemaJoi = Joi.object({
nome: Joi.string().trim().min(3).max(100).messages({
'string.base': 'O nome do produto deve ser uma string.',
'string.empty': 'O nome do produto não pode ser vazio.',
'string.min': 'O nome do produto deve ter no mínimo {#limit} caracteres.',
'string.max': 'O nome do produto deve ter no máximo {#limit} caracteres.'
}),
preco: Joi.number().positive().messages({
'number.base': 'O preço do produto deve ser um número.',
'number.positive': 'O preço do produto deve ser maior que zero.'
})
}).min(1).messages({ 'object.min': 'Pelo menos um campo (nome ou preco) deve ser fornecido para atualização.' }); // Garante que pelo menos um campo seja enviado
module.exports = {
criarUsuarioSchemaJoi,
criarUsuarioSchemaYup,
atualizarProdutoSchemaJoi // EXPORTE O NOVO ESQUEMA
};
- Adicione a rota
PUT /api/produtos/:idemroutes/usuarioRoutes.js. Precisaremos de um middleware para validar oidnos parâmetros e o middlewarevalidatepara o corpo da requisição.
// routes/usuarioRoutes.js (ADICIONE ANTES DE 'module.exports = router;')
// Middleware para validar o ID do produto nos parâmetros da URL
const validateProdutoId = (req, res, next) => {
const id = parseInt(req.params.id, 10); // Converte o ID da URL para número inteiro
if (isNaN(id) || id <= 0) { // Verifica se não é um número ou é menor/igual a zero
console.error("Erro de validação: ID de produto inválido.");
return res.status(400).json({
status: 'erro',
mensagem: 'ID de produto inválido. Deve ser um número inteiro positivo.'
});
}
req.params.id = id; // Substitui o ID da string pelo número inteiro validado
next(); // Continua para o próximo middleware/handler
};
router.put(
'/produtos/:id', // Caminho da rota com parâmetro de ID
validateProdutoId, // Primeiro valida o ID da URL
validate(atualizarProdutoSchemaJoi), // Depois valida o corpo da requisição com Joi
(req, res) => {
const productId = req.params.id; // Pega o ID validado
const { nome, preco } = req.body; // Pega os dados validados do corpo
const produtoIndex = produtos.findIndex(p => p.id === productId); // Encontra o índice do produto
if (produtoIndex === -1) {
console.warn(Produto com ID ${productId} não encontrado para atualização.);
return res.status(404).json({
status: 'erro',
mensagem: Produto com ID ${productId} não encontrado.
});
}
// Atualiza o produto com os novos dados (apenas os que foram fornecidos)
if (nome) produtos[produtoIndex].nome = nome;
if (preco) produtos[produtoIndex].preco = preco;
const produtoAtualizado = produtos[produtoIndex];
res.status(200).json({
status: 'sucesso',
mensagem: 'Produto atualizado com sucesso!',
produto: produtoAtualizado
});
}
);
- Inicie o servidor (
node index.js).
Como Testar e Validar o Resultado
Use cURL ou uma ferramenta como Postman/Insomnia.
Listar produtos existentes:
curl http://localhost:3000/api/usuarios/produtos
Saída esperada: Uma lista de produtos, incluindo o Notebook Gamer (ID 101) e o Mouse Óptico (ID 102).
Cenário 1: Atualização com Sucesso
curl -X PUT -H "Content-Type: application/json" -d '{"nome": "Notebook Pro", "preco": 8000.00}' http://localhost:3000/api/usuarios/produtos/101
Saída esperada: Um JSON com status: 'sucesso' e o produto atualizado.
Cenário 2: Erro de Validação (ID de produto inválido)
curl -X PUT -H "Content-Type: application/json" -d '{"nome": "Teclado Mecânico"}' http://localhost:3000/api/usuarios/produtos/abc
Saída esperada: Um JSON com status: 'erro' e mensagem “ID de produto inválido…”.
Cenário 3: Erro de Validação (nome muito curto, preço negativo)
curl -X PUT -H "Content-Type: application/json" -d '{"nome": "Te", "preco": -50}' http://localhost:3000/api/usuarios/produtos/102
Saída esperada: Um JSON com status: 'erro' e um array erros detalhando as falhas de validação para nome e preco.
Cenário 4: Erro de Validação (corpo vazio)
curl -X PUT -H "Content-Type: application/json" -d '{}' http://localhost:3000/api/usuarios/produtos/102
Saída esperada: Um JSON com status: 'erro' e mensagem Pelo menos um campo (nome ou preco) deve ser fornecido para atualização.
Troubleshooting dos Erros Mais Comuns
- “Cannot read property ‘nome’ of undefined”: Geralmente indica que o
express.json()middleware não está sendo usado, ereq.bodyestá vazio. Verifiqueapp.use(express.json());noindex.js. - “Headers already sent”: Ocorre se você tentar enviar mais de uma resposta HTTP (ex:
res.status().json()e depoisnext()). Certifique-se de usarreturn res.status().json()para encerrar a execução da função após enviar a resposta de erro. - Erros de sintaxe da biblioteca de validação (Joi/Yup/express-validator): Revise a documentação da biblioteca para a sintaxe correta de cada regra e mensagem.
- Middleware não chamado: Se sua validação não está sendo executada, verifique a ordem dos middlewares na sua rota. O middleware de validação deve vir antes do handler da rota.
console.errorvazio para erros: Assegure-se de que a formatação dos erros (error.detailspara Joi/Yup,errors.array()paraexpress-validator) esteja correta para que o log mostre os detalhes.
Próximos Passos Sugeridos
- Validação de Parâmetros de Consulta (
req.query): Implemente validações para filtros de busca (ex:GET /api/produtos?minPreco=X&categoria=Y). - Validação de Upload de Arquivos: Explore bibliotecas como
multere como validar o tipo MIME, tamanho e dimensões de arquivos. - Validação de Autenticação/Autorização: Embora seja um tópico diferente, a validação de tokens JWT ou permissões de usuário frequentemente envolve a verificação de formatos e existência de dados no
req.headers. - Testes Unitários e de Integração: Escreva testes automatizados para seus middlewares de validação, garantindo que eles se comportem como esperado para entradas válidas e inválidas.
- Integração com ORMs/ODMs: Muitas ferramentas de banco de dados (como Mongoose para MongoDB, Sequelize para SQL) possuem suas próprias funcionalidades de validação. Entenda quando usá-las em conjunto com a validação de entrada da API.
🚀 Pronto para a próxima aula?
Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!