Para consumir a API de CPF da CPFHub.io em Supabase Edge Functions, crie uma função Deno que recebe o CPF via POST, consulta o endpoint https://api.cpfhub.io/cpf/{CPF} com o header x-api-key e retorna os dados ao frontend — mantendo a chave de API segura no servidor. A integração com o banco PostgreSQL do Supabase permite implementar cache nativo sem Redis, armazenando resultados por 24 horas. Consulte a documentação oficial do Supabase para detalhes sobre deploy e variáveis de ambiente no Deno Deploy.
Configuração inicial
Primeiro, instale o Supabase CLI e inicialize o projeto:
# Instalar Supabase CLI
npm install -g supabase
# Iniciar projeto (se ainda nao existir)
supabase init
# Criar a Edge Function
supabase functions new validar-cpf
Isso cria a estrutura:
supabase/
functions/
validar-cpf/
index.ts
Implementação da Edge Function
A Edge Function recebe requisições HTTP e consulta a API da CPFHub.io:
// supabase/functions/validar-cpf/index.ts
import { serve } from "https://deno.land/std@0.208.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.0";
interface CPFApiResponse {
success: boolean;
data?: {
cpf: string;
name: string;
nameUpper: string;
gender: string;
birthDate: string;
day: number;
month: number;
year: number;
};
}
const CPFHUB_API_KEY = Deno.env.get("CPFHUB_API_KEY") ?? "";
const SUPABASE_URL = Deno.env.get("SUPABASE_URL") ?? "";
const SUPABASE_SERVICE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") ?? "";
const TIMEOUT_MS = 10000;
const corsHeaders = {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST, OPTIONS",
"Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type",
};
function jsonResponse(body: object, status = 200): Response {
return new Response(JSON.stringify(body), {
status,
headers: { ...corsHeaders, "Content-Type": "application/json" },
});
}
async function consultarCPFHub(cpf: string): Promise<CPFApiResponse> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
try {
const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
method: "GET",
headers: {
"x-api-key": CPFHUB_API_KEY,
"Accept": "application/json",
},
signal: controller.signal,
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(`API retornou status ${response.status}`);
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
async function verificarCache(
supabase: ReturnType<typeof createClient>,
cpf: string
) {
const { data, error } = await supabase
.from("cpf_cache")
.select("*")
.eq("cpf", cpf)
.gt("expires_at", new Date().toISOString())
.single();
if (error || !data) return null;
return data;
}
async function salvarCache(
supabase: ReturnType<typeof createClient>,
cpf: string,
dados: object
) {
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
await supabase.from("cpf_cache").upsert({
cpf,
dados,
expires_at: expiresAt.toISOString(),
updated_at: new Date().toISOString(),
});
}
serve(async (req: Request) => {
// Tratar preflight CORS
if (req.method === "OPTIONS") {
return new Response("ok", { headers: corsHeaders });
}
if (req.method !== "POST") {
return jsonResponse({ erro: "Use POST com body { cpf: '...' }" }, 405);
}
try {
const body = await req.json();
const cpfRaw = body.cpf ?? "";
const cpf = cpfRaw.replace(/\D/g, "");
if (cpf.length !== 11) {
return jsonResponse({ erro: "CPF deve conter 11 digitos" }, 400);
}
// Inicializar cliente Supabase
const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_KEY);
// Verificar cache primeiro
const cached = await verificarCache(supabase, cpf);
if (cached) {
return jsonResponse({
valido: true,
dados: cached.dados,
fonte: "cache",
});
}
// Consultar API CPFHub.io
const resultado = await consultarCPFHub(cpf);
if (!resultado.success || !resultado.data) {
return jsonResponse({ erro: "CPF nao encontrado" }, 404);
}
const dadosFormatados = {
nome: resultado.data.name,
cpf: resultado.data.cpf,
genero: resultado.data.gender,
dataNascimento: resultado.data.birthDate,
};
// Salvar no cache
await salvarCache(supabase, cpf, dadosFormatados);
return jsonResponse({
valido: true,
dados: dadosFormatados,
fonte: "api",
});
} catch (error) {
const mensagem = error instanceof DOMException && error.name === "AbortError"
? "Timeout na consulta de CPF"
: `Erro: ${(error as Error).message}`;
return jsonResponse({ erro: mensagem }, 500);
}
});
Tabela de cache no Supabase
Crie a tabela de cache no banco de dados Supabase para evitar consultas repetidas:
-- Migration: criar tabela de cache de CPF
CREATE TABLE IF NOT EXISTS cpf_cache (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
cpf VARCHAR(11) UNIQUE NOT NULL,
dados JSONB NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
updated_at TIMESTAMPTZ DEFAULT NOW(),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indice para buscas por CPF
CREATE INDEX idx_cpf_cache_cpf ON cpf_cache(cpf);
-- Indice para limpeza de cache expirado
CREATE INDEX idx_cpf_cache_expires ON cpf_cache(expires_at);
-- RLS: apenas service role pode acessar
ALTER TABLE cpf_cache ENABLE ROW LEVEL SECURITY;
-- Funcao para limpar cache expirado (executar via cron)
CREATE OR REPLACE FUNCTION limpar_cache_cpf_expirado()
RETURNS void AS $$
BEGIN
DELETE FROM cpf_cache WHERE expires_at < NOW();
END;
$$ LANGUAGE plpgsql;
Configuração de segredos
Armazene a chave de API como segredo do Supabase:
# Definir segredo para a Edge Function
supabase secrets set CPFHUB_API_KEY=sua_chave_aqui
# Verificar segredos configurados
supabase secrets list
Os segredos ficam disponíveis como variaveis de ambiente via Deno.env.get() dentro da Edge Function.
Deploy e teste
Deploy da Edge Function:
# Deploy
supabase functions deploy validar-cpf
# Teste local antes do deploy
supabase functions serve validar-cpf --env-file .env.local
Teste com curl:
curl -X POST \
"https://SEU_PROJETO.supabase.co/functions/v1/validar-cpf" \
-H "Authorization: Bearer SEU_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"cpf": "12345678901"}'
Integração com o frontend Supabase
No frontend, chame a Edge Function usando o cliente Supabase:
// No frontend (React, Vue, Svelte, etc.)
import { createClient } from "@supabase/supabase-js";
const supabase = createClient(
"https://SEU_PROJETO.supabase.co",
"SUA_ANON_KEY"
);
async function validarCPF(cpf: string) {
const { data, error } = await supabase.functions.invoke("validar-cpf", {
body: { cpf },
});
if (error) {
console.error("Erro na validacao:", error.message);
return null;
}
return data;
}
Essa abordagem mantem a chave de API da CPFHub.io
Vantagens da arquitetura Supabase
A combinação de Edge Functions + banco PostgreSQL do Supabase oferece beneficios únicos:
- Ecossistema unificado -- Banco, autenticação, storage e funções serverless em uma única plataforma.
- Cache nativo -- O banco PostgreSQL serve como cache sem necessidade de Redis ou Memcached.
- RLS (Row Level Security) -- Controle granular de acesso aos dados de CPF cacheados.
- Edge global -- As Edge Functions executam em servidores distribuidos, minimizando latência.
- Open source -- Possibilidade de self-hosting para requisitos de compliance mais rigorosos.
Perguntas frequentes
Como a chave de API fica protegida em uma Supabase Edge Function?
A chave é armazenada como segredo via supabase secrets set e acessada exclusivamente pelo runtime Deno no servidor — nunca exposta no bundle do frontend. O cliente Supabase no navegador só tem acesso à anon key pública, que não carrega permissão para chamar a API de CPF diretamente.
O que acontece se a API de CPF demorar mais do que o esperado?
A função usa AbortController com timeout de 10 segundos. Se a resposta não chegar nesse prazo, o fetch é cancelado e a Edge Function retorna um erro Timeout na consulta de CPF com status 500 — sem travar a requisição indefinidamente. A latência típica da CPFHub.io é de aproximadamente 900ms.
A CPFHub.io bloqueia requisições quando o limite do plano é atingido?
Não. O plano gratuito inclui 50 consultas por mês sem cartão de crédito. Ao ultrapassar o limite, a API continua respondendo normalmente e cobra R$0,15 por consulta adicional — sem retornar erro de limite nem interromper o serviço. O plano Pro oferece 1.000 consultas mensais por R$149.
Como evitar consultas duplicadas desnecessárias à API?
A arquitetura apresentada usa a tabela cpf_cache do PostgreSQL para armazenar resultados por 24 horas. Antes de cada consulta à API, a função verifica se há um registro válido no cache. Essa estratégia reduz o consumo de cota e melhora o tempo de resposta para CPFs consultados com frequência. Veja mais em Como implementar cache inteligente em respostas de API de CPF.
Conclusão
Supabase Edge Functions oferecem uma forma elegante de integrar a validação de CPF em aplicações que ja utilizam o ecossistema Supabase. A combinação de Deno runtime, banco PostgreSQL para cache e segredos gerenciados cria uma solução completa sem infraestrutura adicional. A API da CPFHub.io
Cadastre-se em cpfhub.io
CPFHub.io
Pronto para integrar a API?
50 consultas gratuitas para testar agora. Sem cartão de crédito. Acesso imediato à documentação.
Sobre a redação
Redação CPFHub.io
Time editorial especializado em APIs de CPF, identidade digital e compliance no mercado brasileiro. Produzimos guias técnicos, análises regulatórias e tutoriais sobre LGPD e KYC para desenvolvedores e líderes de produto.



