Introdução
Vue.js 3 trouxe a Composition API como uma nova forma de organizar a lógica de componentes, oferecendo maior reutilização de código e melhor tipagem com TypeScript. Para desenvolvedores que trabalham com formulários de cadastro e checkout no Brasil, a validação de CPF é um requisito recorrente que se beneficia diretamente dessa arquitetura.
A validação de CPF envolve duas etapas: a verificação local dos dígitos verificadores (que pode ser feita sem requisição de rede) e a consulta a uma API externa para confirmar que o CPF realmente existe e obter dados do titular. A CPFHub.io resolve a segunda etapa com uma chamada REST que retorna nome, gênero e data de nascimento do titular. Este tutorial mostra como implementar ambas no Vue.js 3 usando Composition API, com composables reutilizáveis, validação local e integração com a API da CPFHub.io.
Arquitetura da solução
A implementação será organizada em três composables:
-
useCpfValidator -- Validação local dos dígitos verificadores.
-
useCpfLookup -- Consulta à API da CPFHub.io via backend proxy.
-
useCpfForm -- Composable que combina os dois anteriores e gerencia o estado do formulário.
Essa separação permite reutilizar cada composable independentemente em diferentes partes da aplicação.
Composable de validação local
O primeiro composable válida os dígitos verificadores do CPF sem fazer requisição de rede:
// src/composables/useCpfValidator.ts
import { ref, computed } from 'vue';
export function useCpfValidator() {
const cpf = ref('');
const cpfLimpo = computed(() => cpf.value.replace(/\D/g, ''));
const isFormatoValido = computed(() => {
const valor = cpfLimpo.value;
if (valor.length !== 11) return false;
if (/^(\d)\1{10}$/.test(valor)) return false;
// Primeiro dígito verificador
let soma = 0;
for (let i = 0; i < 9; i++) {
soma += parseInt(valor.charAt(i)) * (10 - i);
}
let resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
if (resto !== parseInt(valor.charAt(9))) return false;
// Segundo dígito verificador
soma = 0;
for (let i = 0; i < 10; i++) {
soma += parseInt(valor.charAt(i)) * (11 - i);
}
resto = (soma * 10) % 11;
if (resto === 10) resto = 0;
if (resto !== parseInt(valor.charAt(10))) return false;
return true;
});
const cpfFormatado = computed(() => {
const valor = cpfLimpo.value;
if (valor.length !== 11) return valor;
return `${valor.slice(0, 3)}.${valor.slice(3, 6)}.${valor.slice(6, 9)}-${valor.slice(9)}`;
});
return {
cpf,
cpfLimpo,
isFormatoValido,
cpfFormatado
};
}
Composable de consulta à API
O segundo composable encapsula a chamada à API. Por segurança, a requisição passa por um backend proxy que protege a chave de API:
// src/composables/useCpfLookup.ts
import { ref } from 'vue';
interface CpfData {
cpf: string;
name: string;
nameUpper: string;
gender: string;
birthDate: string;
day: number;
month: number;
year: number;
}
interface CpfResponse {
success: boolean;
data: CpfData;
}
export function useCpfLookup() {
const dados = ref<CpfData | null>(null);
const carregando = ref(false);
const erro = ref<string | null>(null);
async function consultar(cpf: string): Promise<void> {
const cpfLimpo = cpf.replace(/\D/g, '');
carregando.value = true;
erro.value = null;
dados.value = null;
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000);
const response = await fetch(`/api/cpf/${cpfLimpo}`, {
method: 'GET',
headers: { 'Accept': 'application/json' },
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
const mensagens: Record<number, string> = {
400: 'CPF em formato inválido.',
401: 'Erro de autenticação.',
429: 'Muitas requisições. Tente novamente em instantes.',
500: 'Erro no servidor. Tente novamente.'
};
throw new Error(
mensagens[response.status] || `Erro HTTP ${response.status}`
);
}
const resultado: CpfResponse = await response.json();
if (resultado.success) {
dados.value = resultado.data;
} else {
throw new Error('CPF não encontrado.');
}
} catch (e) {
if (e instanceof Error) {
erro.value = e.name === 'AbortError'
? 'Tempo de resposta excedido.'
: e.message;
}
} finally {
carregando.value = false;
}
}
function limpar(): void {
dados.value = null;
erro.value = null;
}
return {
dados,
carregando,
erro,
consultar,
limpar
};
}
Backend proxy para proteger a chave de API
A chave de API nunca deve ficar exposta no frontend. Crie um proxy simples no backend:
// server/api/cpf/[cpf].js (Nuxt 3) ou endpoint Express
export default defineEventHandler(async (event) => {
const cpf = getRouterParam(event, 'cpf');
const response = await fetch(
`https://api.cpfhub.io/cpf/${cpf}`,
{
method: 'GET',
headers: {
'x-api-key': process.env.CPFHUB_API_KEY,
'Accept': 'application/json'
}
}
);
return response.json();
});
Ou com Express:
const express = require('express');
const app = express();
app.get('/api/cpf/:cpf', async (req, res) => {
const { cpf } = req.params;
const response = await fetch(
`https://api.cpfhub.io/cpf/${cpf}`,
{
headers: {
'x-api-key': process.env.CPFHUB_API_KEY,
'Accept': 'application/json'
}
}
);
const data = await response.json();
res.json(data);
});
app.listen(3000);
Composable combinado para o formulário
O terceiro composable combina validação local e consulta remota:
// src/composables/useCpfForm.ts
import { watch } from 'vue';
import { useCpfValidator } from './useCpfValidator';
import { useCpfLookup } from './useCpfLookup';
export function useCpfForm() {
const { cpf, cpfLimpo, isFormatoValido, cpfFormatado } = useCpfValidator();
const { dados, carregando, erro, consultar, limpar } = useCpfLookup();
// Limpar dados da API quando o CPF muda
watch(cpf, () => {
limpar();
});
async function validarEConsultar(): Promise<void> {
if (!isFormatoValido.value) {
return;
}
await consultar(cpfLimpo.value);
}
return {
cpf,
cpfFormatado,
isFormatoValido,
dados,
carregando,
erro,
validarEConsultar
};
}
Componente Vue com o formulário
Com os composables prontos, o componente fica limpo e focado na apresentação:
<!-- src/components/CpfForm.vue -->
<template>
<form @submit.prevent="validarEConsultar">
<div class="campo">
<label for="cpf">CPF</label>
<input
id="cpf"
v-model="cpf"
type="text"
placeholder="000.000.000-00"
maxlength="14"
/>
<span v-if="cpf.length > 0 && !isFormatoValido" class="erro">
CPF inválido.
</span>
</div>
<button
type="submit"
:disabled="!isFormatoValido || carregando"
>
{{ carregando ? 'Consultando...' : 'Consultar CPF' }}
</button>
<div v-if="dados" class="resultado">
<h3>Dados do titular</h3>
<p><strong>Nome:</strong> {{ dados.name }}</p>
<p>
<strong>Genero:</strong>
{{ dados.gender === 'M' ? 'Masculino' : 'Feminino' }}
</p>
<p><strong>Data de nascimento:</strong> {{ dados.birthDate }}</p>
</div>
<div v-if="erro" class="erro-consulta">
<p>{{ erro }}</p>
</div>
</form>
</template>
<script setup lang="ts">
import { useCpfForm } from '../composables/useCpfForm';
const {
cpf,
isFormatoValido,
dados,
carregando,
erro,
validarEConsultar
} = useCpfForm();
</script>
Adicionando máscara de input
Para melhorar a experiência do usuário, adicione formatação automática ao campo de CPF:
// src/composables/useCpfMask.ts
import { ref, watch } from 'vue';
export function useCpfMask() {
const valorBruto = ref('');
const valorMascarado = ref('');
watch(valorBruto, (novo) => {
const numeros = novo.replace(/\D/g, '').slice(0, 11);
let formatado = numeros;
if (numeros.length > 9) {
formatado = `${numeros.slice(0, 3)}.${numeros.slice(3, 6)}.${numeros.slice(6, 9)}-${numeros.slice(9)}`;
} else if (numeros.length > 6) {
formatado = `${numeros.slice(0, 3)}.${numeros.slice(3, 6)}.${numeros.slice(6)}`;
} else if (numeros.length > 3) {
formatado = `${numeros.slice(0, 3)}.${numeros.slice(3)}`;
}
valorMascarado.value = formatado;
});
return {
valorBruto,
valorMascarado
};
}
Testando os composables
A Composition API facilita testes unitários, pois os composables podem ser testados independentemente dos componentes:
// src/composables/__tests__/useCpfValidator.test.ts
import { useCpfValidator } from '../useCpfValidator';
describe('useCpfValidator', () => {
test('deve validar CPF com dígitos corretos', () => {
const { cpf, isFormatoValido } = useCpfValidator();
cpf.value = '52998224725';
expect(isFormatoValido.value).toBe(true);
});
test('deve rejeitar CPF com dígitos repetidos', () => {
const { cpf, isFormatoValido } = useCpfValidator();
cpf.value = '11111111111';
expect(isFormatoValido.value).toBe(false);
});
test('deve rejeitar CPF com tamanho incorreto', () => {
const { cpf, isFormatoValido } = useCpfValidator();
cpf.value = '123456';
expect(isFormatoValido.value).toBe(false);
});
test('deve formatar CPF corretamente', () => {
const { cpf, cpfFormatado } = useCpfValidator();
cpf.value = '52998224725';
expect(cpfFormatado.value).toBe('529.982.247-25');
});
});
Perguntas frequentes
O que é necessário para implementar validação de CPF neste contexto?
A validação de CPF exige uma chamada à API com o número do documento e a chave de autenticação. A CPFHub.io retorna o status do CPF, nome do titular e data de nascimento em menos de 200ms, permitindo a verificação em tempo real durante o cadastro ou transação.
A API CPFHub.io funciona para todos os volumes de consulta?
Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito — ideal para testes e projetos pequenos. Para volumes maiores, o plano Pro inclui 1.000 consultas mensais por R$149. Se o limite for ultrapassado, a API não bloqueia: cobra R$0,15 por consulta adicional.
Como garantir conformidade com a LGPD ao usar uma API de CPF?
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.
Quanto tempo leva para integrar a API CPFHub.io?
A integração básica leva menos de 30 minutos: crie uma conta em cpfhub.io, gere a API key no painel e faça uma chamada GET para https://api.cpfhub.io/cpf/{CPF} com o header x-api-key. A documentação inclui exemplos em Python, Node.js, PHP, Java e outras linguagens.
Conclusão
A Composition API do Vue.js 3 é ideal para organizar a lógica de validação de CPF em composables reutilizáveis. Separando a validação local, a consulta à API e a lógica do formulário em composables distintos, o código fica limpo, testável e fácil de manter. A CPFHub.io fornece os dados cadastrais necessários para completar o fluxo — comece com 50 consultas gratuitas por mês 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.



