Makrosites

Suas ideias em realidade digital!

Validação de dados em API PHP puro: como validar JSON antes de salvar

Validação de dados em API PHP puro: como validar JSON antes de salvar >

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.

Resumo prático: antes de salvar qualquer dado vindo da API, valide campos obrigatórios, formatos, tipos, tamanho mínimo, limites numéricos e regras de negócio. Se algo estiver errado, retorne um JSON padronizado com status HTTP adequado.

Por que validar dados antes de salvar em uma API PHP?

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.

Fluxo ideal de validação em uma API PHP puro

Uma boa API deve seguir um fluxo claro antes de salvar os dados:

  1. Receber a requisição JSON;
  2. Decodificar o corpo da requisição;
  3. Verificar se o JSON é válido;
  4. Validar os campos obrigatórios;
  5. Validar formatos, tipos e regras de negócio;
  6. Retornar erro padronizado se houver problema;
  7. Salvar no banco apenas se tudo estiver válido;
  8. Retornar resposta JSON de sucesso.

Esse fluxo deixa sua API mais segura, mais previsível e mais fácil de consumir por qualquer frontend.

Exemplo de JSON recebido pela API

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:

Recebendo JSON em PHP puro

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.

Criando uma função para responder JSON

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.

Função para validar os dados recebidos

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.

Usando a validação antes de salvar

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
    ]
]);

Por que usar status 422 para erro de validação?

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."
  }
}

Normalizando os dados antes de salvar

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:

<?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.

Exemplo salvando com PDO depois da validação

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.'
    ]);
}
Atenção: em produção, evite retornar a mensagem completa do erro do banco para o usuário. Guarde o erro em log e retorne uma mensagem genérica na API.

Validando se o e-mail já existe no banco

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.'
        ]
    ]);
}

Separando validação em um arquivo próprio

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
    ]);
}

Validação no frontend substitui validação no backend?

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:

Checklist de validação para API PHP

Antes de salvar dados em uma API PHP, revise este checklist:

Boas práticas para resposta de erro em API

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.

Erros comuns ao validar dados em PHP

1. Confiar apenas no JavaScript

O JavaScript ajuda na interface, mas não protege o backend. Sempre valide novamente no PHP.

2. Salvar antes de validar tudo

A validação deve acontecer antes de qualquer operação no banco.

3. Retornar erro em HTML

Se sua API retorna HTML em vez de JSON, o frontend pode quebrar ao tentar interpretar a resposta.

4. Exibir erro real do banco para o usuário

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.

5. Não usar prepared statements

Mesmo com validação, os dados devem ser salvos usando PDO com prepared statements para reduzir riscos de SQL Injection.

Exemplo de resposta final de sucesso

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.

Conclusão

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.

Perguntas frequentes sobre validação de dados em API PHP

Preciso validar os dados no PHP mesmo validando no frontend?

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.

Qual status HTTP usar para erro de validação?

Para JSON inválido, use 400 Bad Request. Para JSON válido com campos incorretos, use 422 Unprocessable Entity.

Posso salvar os dados primeiro e validar depois?

Não é recomendado. A validação deve acontecer antes de qualquer operação de gravação no banco.

Como validar e-mail em PHP?

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';
}

Validação evita SQL Injection?

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.

Devo retornar todos os erros de uma vez ou apenas o primeiro?

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.

Perguntas frequentes sobre validação de dados em API PHP

Preciso validar os dados no PHP mesmo validando no frontend?

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.

Qual status HTTP usar para erro de validação?

Para JSON inválido, use 400 Bad Request. Para JSON válido com campos incorretos, use 422 Unprocessable Entity.

Posso salvar os dados primeiro e validar depois?

Não é recomendado. A validação deve acontecer antes de qualquer operação de gravação no banco.

Como validar e-mail em PHP?

Você pode usar a função nativa filter_var com o filtro FILTER_VALIDATE_EMAIL.

Validação evita SQL Injection?

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.

Devo retornar todos os erros de uma vez ou apenas o primeiro?

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.