Validação Blindada de Dados: Zod + Server Actions
A Confiança é o Inimigo da Segurança
Na engenharia de software corporativa, existe uma lei fundamental: Nunca confie no usuário. Nunca confie no frontend.
Se você montar um formulário HTML bonitinho e pegar os dados diretamente para inserir no Supabase, estará abrindo as portas do seu banco de dados para:
- E-mails mal formatados (
teste@.com). - Números de telefone com caracteres bizarros ou injeções de SQL.
- CPFs com tamanhos inválidos ou dados nulos.
Para garantir que o Supabase receba apenas dados puros e imaculados, adotamos a arquitetura de Validação Blindada usando Next.js Server Actions e a biblioteca Zod.
O Fluxo Amador vs O Fluxo Profissional
O Modo Amador (Proibido) ❌
Criar um formulário, pegar os dados no próprio Client Component (via onChange) e enviar direto pro Supabase usando o cliente do navegador. Além de ser lento, permite manipulação fácil de dados pelo console do navegador, burlando qualquer verificação básica que você colocou no botão "Enviar".
O Padrão Ouro B2B (Zod + Server Actions) ✅
- O formulário roda no frontend (Client Component) ou no Servidor sem validação local complexa.
- O botão de enviar dispara uma Server Action (uma função segura, isolada no servidor da Vercel).
- Essa Server Action passa o dado por um "raio-X matemático" (o Schema Zod).
- Se o Zod rejeitar (ex: "A senha precisa ter 8 caracteres"), a Server Action aborta a missão e devolve o erro para o usuário ver.
- Apenas se o Zod aprovar com 100% de sucesso, os dados são enviados ao Supabase.
Como Implementar a Blindagem
Aqui está o código exato que as IAs e desenvolvedores devem seguir ao arquitetar o envio de dados.
1. Defina as Regras Matemáticas (Zod Schema)
O Zod é a nossa polícia de fronteira. Ele define exatamente o formato que os dados devem ter.
import { z } from 'zod'
// Definimos o contrato de dados:
export const UserRegistrationSchema = z.object({
email: z.string().email("Formato de e-mail inválido."),
name: z.string().min(3, "O nome deve ter no mínimo 3 letras."),
age: z.coerce.number().min(18, "Você precisa ter mais de 18 anos.")
})
2. Crie a Server Action
A Action ("use server") recebe os dados do formulário e joga no Zod antes de sequer encostar no banco.
"use server"
import { createClient } from '@/utils/supabase/server'
import { UserRegistrationSchema } from './schemas' // Nosso schema Zod
// Essa função só executa no Servidor Vercel
export async function registerUserAction(formData: FormData) {
// 1. Extraímos os dados brutos e tentamos validar com o Zod
const rawData = {
email: formData.get('email'),
name: formData.get('name'),
age: formData.get('age'),
}
// 2. O RAIO-X (safeParse impede que o app quebre se houver erro)
const validation = UserRegistrationSchema.safeParse(rawData)
// 3. Se falhou, voltamos imediatamente
if (!validation.success) {
return {
status: "error",
errors: validation.error.flatten().fieldErrors // Envia os erros pro formulário ler
}
}
// 4. Chegou aqui? Os dados estão higienizados, blindados e perfeitamente tipados.
const cleanData = validation.data
const supabase = createClient()
const { error } = await supabase.from('users').insert([cleanData])
if (error) {
return { status: "error", message: "Erro interno no servidor." }
}
return { status: "success", message: "Usuário registrado com sucesso!" }
}
Resumo Operacional
Sempre que a sua Inteligência Artificial for programar a lógica de salvamento de um formulário:
- Proíba o envio direto para o Supabase no lado do cliente.
- Construa um
SchemaZod descrevendo os campos. - Escreva uma
Server Actionque recebe oFormDatae roda um.safeParse(). - A operação no banco de dados acontece única e exclusivamente usando os dados limpos extraídos de dentro do Zod (
validation.data).
Com essa estrutura, sua base de dados nunca mais conhecerá a sensação de armazenar lixo ou dados corrompidos.