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

**Publicado:** 02/08/2026
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-validacao-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

```json
{
 "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

```typescript
// 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

```typescript
// 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

```json
// 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

```tsx
// 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

```json
// 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:

```json
{
 "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](https://developers.vtex.com/docs/guides/vtex-io-documentation-policies) para entender os escopos disponíveis e as restrições por ambiente.

```json
{
 "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:

```bash
# 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:

```typescript
// 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](https://www.gov.br/anpd) orienta que dados de identificação devem ser tratados com o princípio da necessidade.

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [Boas práticas para consumir APIs de CPF de forma segura](https://cpfhub.io/blog/boas-praticas-consumir-apis-cpf-segura)
- [Autenticação em APIs REST: como garantir segurança na consulta de CPF](https://cpfhub.io/blog/autenticacao-apis-rest-seguranca-consulta-cpf)
- [Como evitar chargebacks usando validação de CPF no checkout](https://cpfhub.io/blog/como-evitar-chargebacks-usando-validacao-de-cpf-no-checkout)

---

## 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**](https://www.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](https://www.cpfhub.io/)

