Como integrar validação de CPF em VTEX IO com Service Worker

Aprenda a integrar validação de CPF em lojas VTEX IO usando Service Workers e a API da CPFHub para validação em tempo real.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como integrar validação de CPF em VTEX IO com Service Worker

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 é:

  1. O componente React captura o CPF digitado.
  2. Uma requisição interna é enviada ao Service Worker.
  3. O Service Worker consulta a API da CPFHub.
  4. 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.

Redação CPFHub.io

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.

WhatsAppFale conosco via WhatsApp