Suas ideias em realidade digital!
>
Validar os dados recebidos por uma API é uma das etapas mais importantes antes de salvar qualquer informação no banco de dados.
Em uma API PHP puro, principalmente quando trabalhamos com requisições JSON, não basta apenas receber o conteúdo e executar um
INSERT ou UPDATE. É necessário conferir se os campos obrigatórios foram enviados, se o formato está correto,
se os tipos de dados fazem sentido e se a resposta de erro será clara para quem está consumindo a API.
Neste artigo, você vai aprender como criar uma validação simples, organizada e reutilizável em PHP puro para APIs JSON, sem depender de framework. A ideia é montar uma base que você possa usar em cadastros, logins, formulários, sistemas administrativos, aplicativos Android, integrações externas e qualquer endpoint que receba dados do frontend.
Quando uma API recebe dados externos, ela nunca deve confiar totalmente no conteúdo enviado pelo usuário, aplicativo ou sistema terceiro. Mesmo que o frontend tenha validação com JavaScript, a validação no backend continua sendo obrigatória.
Isso acontece porque qualquer pessoa pode chamar sua API diretamente pelo Postman, Insomnia, cURL, aplicativo mobile ou até por scripts automatizados. Se o backend aceitar qualquer valor, o banco pode receber dados inválidos, incompletos ou até perigosos.
Alguns problemas comuns quando não existe validação no backend:
A validação no backend funciona como uma barreira de proteção entre a requisição e o banco de dados. O dado só deve ser salvo quando estiver correto.
Uma boa API deve seguir um fluxo claro antes de salvar os dados:
Esse fluxo deixa sua API mais segura, mais previsível e mais fácil de consumir por qualquer frontend.
Imagine que sua API tenha um endpoint para cadastrar usuários. O frontend envia um JSON parecido com este:
{
"nome": "João Silva",
"email": "joao@exemplo.com",
"senha": "123456",
"idade": 28,
"ativo": true
}
Antes de salvar, precisamos validar se:
nome foi enviado e não está vazio;email foi enviado e tem formato válido;senha tem tamanho mínimo;idade é um número inteiro maior que zero;ativo é um valor booleano.
Em uma API PHP puro, o conteúdo enviado no corpo da requisição pode ser lido usando php://input.
Depois, usamos json_decode para transformar o JSON em array associativo.
<?php
header('Content-Type: application/json; charset=utf-8');
$input = file_get_contents('php://input');
$dados = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode([
'success' => false,
'message' => 'JSON inválido.',
'errors' => [
'json' => 'O corpo da requisição não contém um JSON válido.'
]
]);
exit;
}
Esse primeiro tratamento evita que a API tente trabalhar com um JSON quebrado.
Quando o corpo da requisição não é válido, o ideal é retornar status 400 Bad Request.
Para evitar repetição de código, vale criar uma função simples para padronizar as respostas da API.
<?php
function jsonResponse(int $statusCode, array $data): void
{
http_response_code($statusCode);
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
Assim, sempre que precisar retornar erro ou sucesso, você usa a mesma estrutura.
Agora vamos criar uma função chamada validarUsuario. Ela recebe os dados da requisição e retorna um array de erros.
Se o array estiver vazio, significa que os dados passaram na validação.
<?php
function validarUsuario(array $dados): array
{
$erros = [];
// Nome obrigatório
if (empty($dados['nome']) || trim($dados['nome']) === '') {
$erros['nome'] = 'O nome é obrigatório.';
} elseif (mb_strlen(trim($dados['nome'])) < 3) {
$erros['nome'] = 'O nome deve ter pelo menos 3 caracteres.';
}
// E-mail obrigatório e formato válido
if (empty($dados['email']) || trim($dados['email']) === '') {
$erros['email'] = 'O e-mail é obrigatório.';
} elseif (!filter_var($dados['email'], FILTER_VALIDATE_EMAIL)) {
$erros['email'] = 'Informe um e-mail válido.';
}
// Senha obrigatória e tamanho mínimo
if (empty($dados['senha'])) {
$erros['senha'] = 'A senha é obrigatória.';
} elseif (strlen($dados['senha']) < 6) {
$erros['senha'] = 'A senha deve ter pelo menos 6 caracteres.';
}
// Idade opcional, mas se enviada precisa ser número inteiro maior que zero
if (isset($dados['idade'])) {
if (!is_int($dados['idade'])) {
$erros['idade'] = 'A idade deve ser um número inteiro.';
} elseif ($dados['idade'] <= 0) {
$erros['idade'] = 'A idade deve ser maior que zero.';
}
}
// Ativo opcional, mas se enviado precisa ser booleano
if (isset($dados['ativo']) && !is_bool($dados['ativo'])) {
$erros['ativo'] = 'O campo ativo deve ser verdadeiro ou falso.';
}
return $erros;
}
Essa função deixa a validação separada da lógica de cadastro. Isso facilita a manutenção e evita misturar regra de entrada com regra de banco.
Com a função de validação pronta, podemos usá-la antes de executar o cadastro.
<?php
header('Content-Type: application/json; charset=utf-8');
function jsonResponse(int $statusCode, array $data): void
{
http_response_code($statusCode);
echo json_encode($data, JSON_UNESCAPED_UNICODE);
exit;
}
function validarUsuario(array $dados): array
{
$erros = [];
if (empty($dados['nome']) || trim($dados['nome']) === '') {
$erros['nome'] = 'O nome é obrigatório.';
} elseif (mb_strlen(trim($dados['nome'])) < 3) {
$erros['nome'] = 'O nome deve ter pelo menos 3 caracteres.';
}
if (empty($dados['email']) || trim($dados['email']) === '') {
$erros['email'] = 'O e-mail é obrigatório.';
} elseif (!filter_var($dados['email'], FILTER_VALIDATE_EMAIL)) {
$erros['email'] = 'Informe um e-mail válido.';
}
if (empty($dados['senha'])) {
$erros['senha'] = 'A senha é obrigatória.';
} elseif (strlen($dados['senha']) < 6) {
$erros['senha'] = 'A senha deve ter pelo menos 6 caracteres.';
}
if (isset($dados['idade'])) {
if (!is_int($dados['idade'])) {
$erros['idade'] = 'A idade deve ser um número inteiro.';
} elseif ($dados['idade'] <= 0) {
$erros['idade'] = 'A idade deve ser maior que zero.';
}
}
if (isset($dados['ativo']) && !is_bool($dados['ativo'])) {
$erros['ativo'] = 'O campo ativo deve ser verdadeiro ou falso.';
}
return $erros;
}
// Recebe JSON
$input = file_get_contents('php://input');
$dados = json_decode($input, true);
// Verifica JSON inválido
if (json_last_error() !== JSON_ERROR_NONE) {
jsonResponse(400, [
'success' => false,
'message' => 'JSON inválido.',
'errors' => [
'json' => 'O corpo da requisição não contém um JSON válido.'
]
]);
}
// Verifica se o corpo é um objeto/array válido
if (!is_array($dados)) {
jsonResponse(400, [
'success' => false,
'message' => 'Requisição inválida.',
'errors' => [
'body' => 'Envie um objeto JSON válido.'
]
]);
}
// Valida os dados
$erros = validarUsuario($dados);
if (!empty($erros)) {
jsonResponse(422, [
'success' => false,
'message' => 'Existem campos inválidos.',
'errors' => $erros
]);
}
// Aqui os dados estão válidos
jsonResponse(201, [
'success' => true,
'message' => 'Usuário validado com sucesso. Agora os dados podem ser salvos.',
'data' => [
'nome' => trim($dados['nome']),
'email' => strtolower(trim($dados['email'])),
'idade' => $dados['idade'] ?? null,
'ativo' => $dados['ativo'] ?? true
]
]);
Quando o JSON está malformado, o erro faz parte da estrutura da requisição. Nesse caso, o status 400 Bad Request faz sentido.
Já quando o JSON está correto, mas os dados enviados não passam nas regras da aplicação, o status mais indicado é
422 Unprocessable Entity.
Exemplo:
{
"nome": "",
"email": "email-invalido",
"senha": "123"
}
Esse JSON é válido, mas os valores não são aceitos pela regra da API. Por isso, a resposta pode ser:
{
"success": false,
"message": "Existem campos inválidos.",
"errors": {
"nome": "O nome é obrigatório.",
"email": "Informe um e-mail válido.",
"senha": "A senha deve ter pelo menos 6 caracteres."
}
}
Depois de validar, também é recomendado normalizar os dados antes de enviar para o banco. Normalizar significa ajustar os valores para um padrão esperado.
Exemplos comuns:
trim;strtolower;<?php
$usuario = [
'nome' => trim($dados['nome']),
'email' => strtolower(trim($dados['email'])),
'senha' => password_hash($dados['senha'], PASSWORD_DEFAULT),
'idade' => $dados['idade'] ?? null,
'ativo' => $dados['ativo'] ?? true
];
Essa etapa ajuda a manter o banco mais limpo e evita registros duplicados por diferenças simples, como letras maiúsculas no e-mail.
Depois que os dados foram validados e normalizados, você pode salvar no banco com PDO e prepared statements.
<?php
try {
$pdo = new PDO(
'mysql:host=localhost;dbname=sua_base;charset=utf8mb4',
'usuario',
'senha',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
);
$sql = "INSERT INTO usuarios (nome, email, senha, idade, ativo)
VALUES (:nome, :email, :senha, :idade, :ativo)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':nome' => $usuario['nome'],
':email' => $usuario['email'],
':senha' => $usuario['senha'],
':idade' => $usuario['idade'],
':ativo' => $usuario['ativo'] ? 1 : 0
]);
jsonResponse(201, [
'success' => true,
'message' => 'Usuário cadastrado com sucesso.',
'data' => [
'id' => $pdo->lastInsertId()
]
]);
} catch (PDOException $e) {
jsonResponse(500, [
'success' => false,
'message' => 'Erro interno ao salvar os dados.'
]);
}
Algumas validações dependem do banco de dados. Por exemplo, verificar se o e-mail já está cadastrado.
<?php
function emailJaExiste(PDO $pdo, string $email): bool
{
$sql = "SELECT COUNT(*) AS total FROM usuarios WHERE email = :email";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':email' => $email
]);
$resultado = $stmt->fetch();
return (int)$resultado['total'] > 0;
}
Depois, você pode chamar essa validação antes de salvar:
<?php
if (emailJaExiste($pdo, $usuario['email'])) {
jsonResponse(422, [
'success' => false,
'message' => 'Existem campos inválidos.',
'errors' => [
'email' => 'Este e-mail já está cadastrado.'
]
]);
}
Conforme sua API cresce, não é ideal deixar todas as validações dentro do mesmo arquivo do endpoint. Uma estrutura simples pode ser:
/api
/helpers
response.php
/validators
UsuarioValidator.php
/controllers
UsuarioController.php
index.php
Um exemplo simples de arquivo UsuarioValidator.php:
<?php
class UsuarioValidator
{
public static function validarCadastro(array $dados): array
{
$erros = [];
if (empty($dados['nome']) || trim($dados['nome']) === '') {
$erros['nome'] = 'O nome é obrigatório.';
}
if (empty($dados['email']) || !filter_var($dados['email'], FILTER_VALIDATE_EMAIL)) {
$erros['email'] = 'Informe um e-mail válido.';
}
if (empty($dados['senha']) || strlen($dados['senha']) < 6) {
$erros['senha'] = 'A senha deve ter pelo menos 6 caracteres.';
}
return $erros;
}
}
E no endpoint:
<?php
require_once __DIR__ . '/../validators/UsuarioValidator.php';
$erros = UsuarioValidator::validarCadastro($dados);
if (!empty($erros)) {
jsonResponse(422, [
'success' => false,
'message' => 'Existem campos inválidos.',
'errors' => $erros
]);
}
Não. A validação no frontend melhora a experiência do usuário, mas não protege a API.
O frontend pode avisar rapidamente que o e-mail está inválido ou que a senha é muito curta. Porém, o backend ainda precisa validar tudo novamente, porque qualquer pessoa pode ignorar o frontend e chamar a API diretamente.
A regra prática é simples:
Antes de salvar dados em uma API PHP, revise este checklist:
true ou false?Uma resposta de erro bem estruturada facilita muito o trabalho de quem consome a API. Evite retornar apenas uma string simples como:
Erro ao cadastrar usuário
Prefira uma estrutura JSON organizada:
{
"success": false,
"message": "Existem campos inválidos.",
"errors": {
"nome": "O nome é obrigatório.",
"email": "Informe um e-mail válido."
}
}
Esse formato permite que o frontend exiba mensagens específicas em cada campo do formulário.
O JavaScript ajuda na interface, mas não protege o backend. Sempre valide novamente no PHP.
A validação deve acontecer antes de qualquer operação no banco.
Se sua API retorna HTML em vez de JSON, o frontend pode quebrar ao tentar interpretar a resposta.
Mensagens do banco podem revelar nomes de tabelas, campos e detalhes internos da aplicação. Em produção, retorne uma mensagem genérica e registre o erro em log.
Mesmo com validação, os dados devem ser salvos usando PDO com prepared statements para reduzir riscos de SQL Injection.
Quando tudo estiver correto, sua API pode retornar algo assim:
{
"success": true,
"message": "Usuário cadastrado com sucesso.",
"data": {
"id": 15
}
}
Esse padrão deixa claro para o frontend que a operação deu certo e ainda permite enviar os dados necessários para continuar o fluxo.
Validar dados em uma API PHP puro é uma etapa essencial para evitar erros, proteger o banco de dados e melhorar a qualidade da aplicação. Sempre que sua API receber JSON, o ideal é verificar se o corpo da requisição é válido, validar os campos obrigatórios, conferir formatos, normalizar os dados e só então salvar.
Com uma função de validação bem organizada, sua API fica mais segura, mais fácil de manter e mais preparada para crescer. Mesmo sem usar framework, é possível criar uma estrutura profissional em PHP puro com respostas JSON padronizadas, status HTTP corretos e regras de validação reutilizáveis.
Sim. A validação no frontend melhora a experiência do usuário, mas a validação no PHP protege a API e o banco de dados. Nunca confie apenas na validação feita pelo navegador.
Para JSON inválido, use 400 Bad Request. Para JSON válido com campos incorretos, use 422 Unprocessable Entity.
Não é recomendado. A validação deve acontecer antes de qualquer operação de gravação no banco.
Você pode usar a função nativa filter_var com o filtro FILTER_VALIDATE_EMAIL.
<?php
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
echo 'E-mail inválido';
}
A validação ajuda a reduzir entradas inválidas, mas não substitui prepared statements. Para proteger contra SQL Injection, use PDO com parâmetros preparados.
Para formulários e APIs consumidas por frontend, normalmente é melhor retornar todos os erros de uma vez. Assim o usuário corrige tudo sem precisar enviar várias tentativas.
Sim. A validação no frontend melhora a experiência do usuário, mas a validação no PHP protege a API e o banco de dados. Nunca confie apenas na validação feita pelo navegador.
Para JSON inválido, use 400 Bad Request. Para JSON válido com campos incorretos, use 422 Unprocessable Entity.
Não é recomendado. A validação deve acontecer antes de qualquer operação de gravação no banco.
Você pode usar a função nativa filter_var com o filtro FILTER_VALIDATE_EMAIL.
A validação ajuda a reduzir entradas inválidas, mas não substitui prepared statements. Para proteger contra SQL Injection, use PDO com parâmetros preparados.
Para formulários e APIs consumidas por frontend, normalmente é melhor retornar todos os erros de uma vez. Assim o usuário corrige tudo sem precisar enviar várias tentativas.