Como implementar debounce em campos de CPF que consultam API em tempo real

Aprenda a implementar debounce em campos de CPF que consultam API em tempo real. Evite requisições excessivas e melhore a experiência do usuário.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como implementar debounce em campos de CPF que consultam API em tempo real

Implementar debounce em campos de CPF que consultam API em tempo real significa atrasar a requisição até o usuário parar de digitar, evitando disparar dezenas de chamadas desnecessárias a cada tecla. Com um delay de 500–600ms e validação sintática no front-end, é possível reduzir o consumo de créditos da API em até 90% sem prejudicar a experiência do usuário.

Introdução

Formulários que consultam uma API de CPF em tempo real — enquanto o usuário digita — oferecem uma experiência dinâmica e ágil. No entanto, sem o uso de debounce, cada tecla pressionada pode disparar uma requisição à API, resultando em dezenas de chamadas desnecessárias que consomem créditos e sobrecarregam o servidor. Este guia mostra como implementar debounce em JavaScript puro e React, com a consulta sendo feita no backend.

O que é debounce

Debounce é uma técnica que atrasa a execução de uma função até que um período de inatividade tenha passado. Em vez de executar a função a cada evento (como cada tecla digitada), o debounce aguarda o usuário parar de digitar por um intervalo definido antes de executar a ação.

Sem debounce

Quando o usuário digita o CPF "12345678900", o campo dispara 11 requisições:

Digitou "1" -> Requisição 1
Digitou "12" -> Requisição 2
Digitou "123" -> Requisição 3
...
Digitou "12345678900" -> Requisição 11

Resultado: 10 requisições desperdiçadas, apenas a última é útil.

Com debounce (500ms)

O campo aguarda 500ms após a última tecla antes de disparar a requisição:

Digitou "1" -> Aguarda 500ms...
Digitou "12" -> Reinicia timer, aguarda 500ms...
Digitou "123" -> Reinicia timer, aguarda 500ms...
...
Digitou "12345678900" -> Aguarda 500ms... -> Requisição 1 (única)

Resultado: 1 requisição, economia de 10 créditos.

Implementação em JavaScript puro

Função de debounce

function debounce(fn, delay) {
    let timerId = null;

    return function (...args) {
    if (timerId) {
    clearTimeout(timerId);
    }

    timerId = setTimeout(() => {
    fn.apply(this, args);
    timerId = null;
    }, delay);
    };
}

Aplicando ao campo de CPF

const campoCPF = document.getElementById('cpf-input');
const resultadoDiv = document.getElementById('resultado');

async function consultarCPFNoBackend(cpf) {
    // A consulta à API deve ser feita pelo backend
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
    const response = await fetch(`/api/consultar-cpf?cpf=${cpf}`, {
    signal: controller.signal
    });

    clearTimeout(timeoutId);
    const dados = await response.json();

    if (dados.success) {
    resultadoDiv.textContent = `Nome: ${dados.data.name}`;
    resultadoDiv.className = 'sucesso';
    } else {
    resultadoDiv.textContent = 'CPF não encontrado.';
    resultadoDiv.className = 'erro';
    }
    } catch (error) {
    clearTimeout(timeoutId);
    resultadoDiv.textContent = 'Erro ao consultar CPF.';
    resultadoDiv.className = 'erro';
    }
}

function validarEConsultar(event) {
    const cpf = event.target.value.replace(/\D/g, '');

    if (cpf.length < 11) {
    resultadoDiv.textContent = '';
    return;
    }

    if (cpf.length === 11) {
    consultarCPFNoBackend(cpf);
    }
}

// Debounce de 600ms
campoCPF.addEventListener('input', debounce(validarEConsultar, 600));

Endpoint no backend (Express)

const express = require('express');
const app = express();

app.get('/api/consultar-cpf', async (req, res) => {
    const cpf = req.query.cpf;

    if (!cpf || cpf.length !== 11) {
    return res.status(400).json({ error: 'CPF inválido' });
    }

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
    const response = await fetch(
    `https://api.cpfhub.io/cpf/${cpf}`,
    {
    headers: {
    'x-api-key': process.env.CPFHUB_API_KEY,
    'Accept': 'application/json'
    },
    signal: controller.signal
    }
    );

    clearTimeout(timeoutId);
    const dados = await response.json();
    res.json(dados);

    } catch (error) {
    clearTimeout(timeoutId);
    res.status(500).json({ error: 'Falha na consulta' });
    }
});

app.listen(3000);

Implementação em React

import { useState, useCallback, useRef } from 'react';

function useDebounce(fn, delay) {
    const timerRef = useRef(null);

    return useCallback((...args) => {
    if (timerRef.current) {
    clearTimeout(timerRef.current);
    }

    timerRef.current = setTimeout(() => {
    fn(...args);
    }, delay);
    }, [fn, delay]);
}

function CampoCPF() {
    const [cpf, setCpf] = useState('');
    const [resultado, setResultado] = useState(null);
    const [carregando, setCarregando] = useState(false);

    const consultarCPF = useCallback(async (valor) => {
    const cpfLimpo = valor.replace(/\D/g, '');
    if (cpfLimpo.length !== 11) {
    setResultado(null);
    return;
    }

    setCarregando(true);

    try {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    const response = await fetch(
    `/api/consultar-cpf?cpf=${cpfLimpo}`,
    { signal: controller.signal }
    );

    clearTimeout(timeoutId);
    const dados = await response.json();
    setResultado(dados);
    } catch (error) {
    setResultado({ error: 'Falha na consulta' });
    } finally {
    setCarregando(false);
    }
    }, []);

    const consultarComDebounce = useDebounce(consultarCPF, 600);

    const handleChange = (e) => {
    const valor = e.target.value;
    setCpf(valor);
    consultarComDebounce(valor);
    };

    return (
    <div>
    <input
    type="text"
    value={cpf}
    onChange={handleChange}
    placeholder="Digite o CPF"
    maxLength={14}
    />
    {carregando && <p>Consultando...</p>}
    {resultado?.success && <p>Nome: {resultado.data.name}</p>}
    {resultado?.error && <p>Erro: {resultado.error}</p>}
    </div>
    );
}

Escolhendo o delay ideal

O delay do debounce deve equilibrar responsividade com economia de requisições:

DelayComportamentoQuando usar
300msMuito responsivo, mais requisiçõesCampos com autocomplete
500-600msEquilíbrio idealCampos de CPF (recomendado)
1000msMenos responsivo, menos requisiçõesProcessos batch ou de baixa urgência

Para campos de CPF, 500-600ms é o valor recomendado. O usuário digita os 11 dígitos em aproximadamente 3-5 segundos, e o debounce disparará a consulta logo após ele terminar de digitar.

Combinando debounce com validação sintática

Antes de enviar a requisição ao backend, valide o CPF sintaticamente no front-end para evitar chamadas desnecessárias:

function validarCPFSintatico(cpf) {
    cpf = cpf.replace(/\D/g, '');
    if (cpf.length !== 11) return false;
    if (/^(\d)\1{10}$/.test(cpf)) return false;

    let soma = 0;
    for (let i = 0; i < 9; i++) soma += parseInt(cpf[i]) * (10 - i);
    let resto = (soma * 10) % 11;
    if (resto === 10) resto = 0;
    if (resto !== parseInt(cpf[9])) return false;

    soma = 0;
    for (let i = 0; i < 10; i++) soma += parseInt(cpf[i]) * (11 - i);
    resto = (soma * 10) % 11;
    if (resto === 10) resto = 0;
    return resto === parseInt(cpf[10]);
}

const consultarComDebounce = debounce((valor) => {
    const cpfLimpo = valor.replace(/\D/g, '');

    if (cpfLimpo.length !== 11) return;

    // Validar formato antes de consultar
    if (!validarCPFSintatico(cpfLimpo)) {
    resultadoDiv.textContent = 'CPF com formato inválido.';
    return;
    }

    consultarCPFNoBackend(cpfLimpo);
}, 600);

Boas práticas

  • Sempre consulte a API pelo backend -- Nunca exponha a chave de API no front-end. O debounce controla o momento da chamada, mas a requisição deve partir do servidor.

  • Mostre indicador de carregamento -- Enquanto a consulta está em andamento, exiba um spinner ou texto "Consultando..." para que o usuário saiba que algo está acontecendo.

  • Cancele requisições anteriores -- Se o usuário digitar um novo CPF antes da resposta chegar, cancele a requisição anterior usando AbortController.

  • Valide sintaticamente no front-end -- Rejeite CPFs com formato inválido antes de enviar ao backend.

  • Use máscara de input -- Formate o campo como XXX.XXX.XXX-XX para facilitar a digitação e prevenir erros.

Perguntas frequentes

Qual o delay de debounce ideal para campos de CPF?

Para campos de CPF, o intervalo de 500–600ms é o equilíbrio ideal entre responsividade e economia de requisições. O usuário leva em média 3–5 segundos para digitar 11 dígitos, então o debounce dispara naturalmente logo após ele concluir. Valores abaixo de 300ms aumentam o consumo de créditos; acima de 1.000ms deixam o formulário lento.

A API CPFHub.io bloqueia quando o limite do plano é atingido?

Não. A CPFHub.io nunca retorna HTTP 429 nem bloqueia requisições. Ao ultrapassar o limite do plano, cada consulta extra é cobrada a R$0,15. O plano gratuito oferece 50 consultas/mês sem cartão de crédito; o plano Pro inclui 1.000 consultas por R$149/mês com excedente a 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.

Devo validar o CPF sintaticamente antes de chamar a API?

Sim, sempre. A validação sintática no front-end — verificação dos dígitos verificadores pelo algoritmo da Receita Federal — descarta CPFs inválidos antes mesmo de chegar ao backend, economizando requisições e melhorando a UX com feedback imediato ao usuário.


Conclusão

O debounce é uma técnica simples mas essencial para campos de CPF que consultam API em tempo real. Ao aguardar o usuário terminar de digitar antes de disparar a requisição, ele economiza créditos de API e mantém a experiência fluida. Combinado com validação sintática no front-end e consulta no backend, o resultado é uma integração eficiente e segura.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e implemente debounce em seus formulários de CPF em menos de 30 minutos.

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