Para integrar validação de CPF em VTEX IO, crie um Service Worker (builder node) que recebe o CPF do componente React, consulta a API da CPFHub.io via GET https://api.cpfhub.io/cpf/{CPF} com o header x-api-key, e retorna o resultado ao frontend. A chave de API fica protegida no backend, e a resposta chega em aproximadamente 900ms. A política outbound-access no manifest libera a comunicação com api.cpfhub.io na infraestrutura VTEX.
Arquitetura da solução em VTEX IO
A VTEX IO separa frontend e backend em dois builders:
- react: componentes React que renderizam no storefront.
- node: serviços backend (Service Workers) que executam no servidor VTEX.
A comunicação entre eles ocorre via rotas internas da VTEX IO. O fluxo da validação é:
- O componente React captura o CPF digitado.
- Uma requisição interna é enviada ao Service Worker.
- O Service Worker consulta a API da CPFHub.
- O resultado é retornado ao componente React.
Essa arquitetura garante que a chave de API fique protegida no backend.
Criando a aplicação VTEX IO
Manifest do projeto
{
"vendor": "suaempresa",
"name": "cpf-validator",
"version": "0.1.0",
"builders": {
"react": "3.x",
"node": "6.x",
"messages": "1.x",
"docs": "0.x"
},
"dependencies": {
"vtex.styleguide": "9.x"
},
"policies": [
{
"name": "outbound-access",
"attrs": {
"host": "api.cpfhub.io",
"path": "/cpf/*"
}
}
],
"settingsSchema": {
"title": "CPFHub Validator",
"type": "object",
"properties": {
"apiKey": {
"title": "Chave de API CPFHub",
"type": "string"
}
}
}
}
Implementando o Service Worker (backend)
Handler de validação
// node/handlers/validateCpf.ts
import type { ServiceContext } from "@vtex/api";
export async function validateCpf(ctx: ServiceContext) {
const {
vtex: {
route: {
params: { cpf },
},
},
clients: { apps },
} = ctx;
const cleanCpf = String(cpf).replace(/\D/g, "");
if (cleanCpf.length !== 11) {
ctx.status = 400;
ctx.body = {
success: false,
message: "CPF deve conter 11 dígitos.",
};
return;
}
try {
const appSettings = await apps.getAppSettings(
process.env.VTEX_APP_ID as string
);
const apiKey = appSettings.apiKey;
if (!apiKey) {
ctx.status = 500;
ctx.body = {
success: false,
message: "Chave de API não configurada.",
};
return;
}
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch(
`https://api.cpfhub.io/cpf/${cleanCpf}`,
{
method: "GET",
headers: {
"x-api-key": apiKey,
Accept: "application/json",
},
signal: controller.signal,
}
);
clearTimeout(timeoutId);
const data = await response.json();
ctx.status = response.ok ? 200 : response.status;
ctx.body = data;
} catch (error: any) {
if (error.name === "AbortError") {
ctx.status = 504;
ctx.body = {
success: false,
message: "Tempo de resposta excedido.",
};
} else {
ctx.status = 500;
ctx.body = {
success: false,
message: "Erro interno ao validar CPF.",
};
}
}
}
Configuração do serviço
// node/index.ts
import type {
RecorderState,
ServiceContext,
ParamsContext,
} from "@vtex/api";
import { Service } from "@vtex/api";
import { validateCpf } from "./handlers/validateCpf";
export default new Service<ServiceContext, RecorderState, ParamsContext>({
routes: {
validateCpf: {
path: "/_v/cpf-validator/validate/:cpf",
public: true,
methods: ["GET"],
handler: validateCpf,
},
},
});
Service Node configuration
// node/service.json
{
"memory": 128,
"timeout": 15,
"minReplicas": 1,
"maxReplicas": 4,
"routes": {
"validateCpf": {
"path": "/_v/cpf-validator/validate/:cpf",
"public": true
}
}
}
Implementando o componente React (frontend)
Componente de validação de CPF
// react/components/CpfValidator.tsx
import React, { useState, useCallback, useRef } from "react";
import { Input, Spinner, IconCheck, IconDeny } from "vtex.styleguide";
interface CpfData {
cpf: string;
name: string;
birthDate: string;
gender: string;
}
const CpfValidator: React.FC = () => {
const [cpf, setCpf] = useState("");
const [loading, setLoading] = useState(false);
const [result, setResult] = useState<CpfData | null>(null);
const [error, setError] = useState("");
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const formatCpf = (value: string): string => {
const digits = value.replace(/\D/g, "").slice(0, 11);
if (digits.length > 9) {
return digits.replace(
/(\d{3})(\d{3})(\d{3})(\d{1,2})/,
"$1.$2.$3-$4"
);
}
if (digits.length > 6) {
return digits.replace(/(\d{3})(\d{3})(\d{1,3})/, "$1.$2.$3");
}
if (digits.length > 3) {
return digits.replace(/(\d{3})(\d{1,3})/, "$1.$2");
}
return digits;
};
const validateCpf = useCallback(async (cleanCpf: string) => {
setLoading(true);
setError("");
setResult(null);
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 12000);
try {
const response = await fetch(
`/_v/cpf-validator/validate/${cleanCpf}`,
{
signal: controller.signal,
}
);
clearTimeout(timeoutId);
const data = await response.json();
if (data.success) {
setResult(data.data);
} else {
setError(data.message || "CPF não encontrado.");
}
} catch (err: any) {
clearTimeout(timeoutId);
if (err.name === "AbortError") {
setError("Tempo de resposta excedido.");
} else {
setError("Erro ao validar CPF.");
}
} finally {
setLoading(false);
}
}, []);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const formatted = formatCpf(e.target.value);
setCpf(formatted);
const digits = formatted.replace(/\D/g, "");
if (debounceRef.current) clearTimeout(debounceRef.current);
if (digits.length === 11) {
debounceRef.current = setTimeout(() => {
validateCpf(digits);
}, 500);
} else {
setResult(null);
setError("");
}
};
return (
<div className="cpf-validator-container pa4">
<div className="mb4">
<Input
label="CPF"
placeholder="000.000.000-00"
value={cpf}
onChange={handleChange}
suffix={
loading ? (
<Spinner size={20} />
) : result ? (
<IconCheck />
) : error ? (
<IconDeny />
) : null
}
/>
</div>
{error && (
<div className="c-danger t-small mt2">{error}</div>
)}
{result && (
<div className="bg-muted-5 pa4 br3 mt4">
<div className="mb3">
<span className="fw6">Nome:</span> {result.name}
</div>
<div className="mb3">
<span className="fw6">Data de Nascimento:</span>{" "}
{result.birthDate}
</div>
<div>
<span className="fw6">Genero:</span> {result.gender}
</div>
</div>
)}
</div>
);
};
export default CpfValidator;
Registrando o componente no Store Framework
Interface declaration
// store/interfaces.json
{
"cpf-validator": {
"component": "CpfValidator",
"composition": "children"
}
}
Usando no tema da loja
No tema VTEX IO da loja, adicione o bloco no local desejado:
{
"store.custom#registration": {
"blocks": ["cpf-validator"]
}
}
Configuração de políticas de acesso
A VTEX IO usa políticas de acesso para controlar chamadas externas. A política outbound-access no manifest permite que o Service Worker se comunique com a API da CPFHub. Consulte a documentação de políticas de outbound da VTEX para entender os escopos disponíveis e as restrições por ambiente.
{
"policies": [
{
"name": "outbound-access",
"attrs": {
"host": "api.cpfhub.io",
"path": "/cpf/*"
}
}
]
}
Sem essa política, qualquer requisição para api.cpfhub.io será bloqueada pela infraestrutura VTEX.
Deploy e configuração
Após desenvolver, publique a aplicação:
# Login na VTEX IO
vtex login suaempresa
# Modo de desenvolvimento
vtex link
# Publicar versão
vtex publish
# Instalar no workspace
vtex install suaempresa.cpf-validator@0.1.0
# Configurar a chave de API via admin
# Acesse: Admin > Apps > CPFHub Validator
Configure a chave de API no painel administrativo da VTEX em Apps > CPFHub Validator.
Considerações de performance e cache
Para otimizar o desempenho em lojas com alto tráfego, considere implementar cache no Service Worker:
// Adicione ao handler
const cacheKey = `cpf:${cleanCpf}`;
const cached = await ctx.clients.vbase
.getJSON("cpf-cache", cacheKey)
.catch(() => null);
if (cached) {
ctx.status = 200;
ctx.body = cached;
return;
}
// Após consulta bem-sucedida
await ctx.clients.vbase.saveJSON("cpf-cache", cacheKey, data);
Perguntas frequentes
Como funciona a autenticação na API CPFHub.io dentro do VTEX IO?
A autenticação usa o header x-api-key com a chave gerada no painel da CPFHub.io. No VTEX IO, o valor da chave é armazenado nas configurações do aplicativo (settingsSchema) e recuperado pelo handler via apps.getAppSettings(), mantendo-a fora do código-fonte e do frontend. Nunca exponha a chave diretamente no componente React.
Qual é a latência esperada ao consultar a API CPFHub.io em produção?
A API CPFHub.io responde em aproximadamente 900ms. Configure o timeout do AbortController no Service Worker acima disso — o exemplo usa 10 segundos — para absorver variações de rede sem derrubar a requisição prematuramente. O uso de cache via VBase reduz chamadas repetidas ao mesmo CPF.
O que acontece se o limite de consultas gratuitas for atingido?
O plano gratuito oferece 50 consultas por mês. Ao atingir esse limite, a API não bloqueia as requisições: cada consulta adicional é cobrada a R$0,15. Para volumes maiores, o plano Pro cobre 1.000 consultas mensais por R$149, com o mesmo custo adicional por excedente.
Como garantir conformidade com a LGPD ao usar uma API de CPF em VTEX IO?
Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário (não guarde o CPF cru se um token bastar), implemente controle de acesso aos logs de consulta e documente a base legal para o tratamento. A ANPD orienta que dados de identificação devem ser tratados com o princípio da necessidade.
Conclusão
A integração de validação de CPF em VTEX IO com Service Workers combina a robustez do ecossistema VTEX com a confiabilidade da API do CPFHub.io
A API da CPFHub entrega respostas em aproximadamente 900ms, com uptime de 99,9% e conformidade total com a LGPD -- ideal para operações de e-commerce na VTEX.
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.



