Para processar consultas de CPF em escala com Node.js sem bloquear o servidor, crie um worker BullMQ que consome uma fila Redis: o endpoint responde imediatamente com um jobId, enquanto o worker consulta a API da CPFHub.io em background com retry automático, controle de concorrência e notificação por callback. Essa arquitetura suporta desde dezenas até milhares de CPFs por hora sem gargalos.
Introdução
Processar consultas de CPF de forma síncrona durante o request/response pode ser aceitável para operações individuais, mas se torna um gargalo quando o volume cresce. Filas de processamento permitem desacoplar a requisição do processamento, garantindo que o sistema responda rapidamente enquanto os CPFs são validados em segundo plano. O BullMQ, baseado em Redis, é a solução mais robusta para filas em Node.js.
Arquitetura do sistema com filas
A arquitetura separa produtores (que enfileiram CPFs) de consumidores (workers que processam).
| Componente | Responsabilidade | Tecnologia |
|---|---|---|
| Produtor | Recebe requisições e enfileira CPFs | Express + BullMQ |
| Fila | Armazena jobs pendentes com prioridade | Redis + BullMQ |
| Worker | Consome jobs e consulta a API de CPF | BullMQ Worker |
| Dashboard | Monitora status das filas | Bull Board |
Desacoplamento -- a API responde imediatamente ao cliente enquanto o processamento ocorre em background.
Resiliência -- se um worker falhar, o job é automaticamente reenfileirado para retry.
Escalabilidade -- múltiplos workers podem processar a mesma fila em paralelo.
Configurando a fila e o produtor
Primeiro, configure a fila BullMQ e o endpoint que enfileira CPFs para processamento.
import { Queue } from "bullmq";
import express from "express";
const conexaoRedis = {
host: process.env.REDIS_HOST || "127.0.0.1",
port: parseInt(process.env.REDIS_PORT) || 6379
};
const filaCPF = new Queue("consulta-cpf", { connection: conexaoRedis });
const app = express();
app.use(express.json());
// Endpoint para enfileirar um único CPF
app.post("/api/consultar-cpf", async (req, res) => {
const { cpf, callback_url } = req.body;
const job = await filaCPF.add(
"consultar",
{ cpf, callback_url },
{
attempts: 3,
backoff: { type: "exponential", delay: 2000 },
removeOnComplete: { age: 3600 },
removeOnFail: { age: 86400 }
}
);
res.status(202).json({
mensagem: "Consulta enfileirada com sucesso",
jobId: job.id,
status: "pendente"
});
});
// Endpoint para enfileirar lote de CPFs
app.post("/api/consultar-cpf/lote", async (req, res) => {
const { cpfs, prioridade } = req.body;
const jobs = await filaCPF.addBulk(
cpfs.map((cpf, index) => ({
name: "consultar",
data: { cpf, loteId: Date.now().toString() },
opts: {
priority: prioridade || 5,
attempts: 3,
backoff: { type: "exponential", delay: 2000 },
delay: index * 100 // Espaçar requisições
}
}))
);
res.status(202).json({
mensagem: `${jobs.length} CPFs enfileirados`,
jobIds: jobs.map((j) => j.id)
});
});
app.listen(3000, () => console.log("Servidor rodando na porta 3000"));
Implementando o worker
O worker consome jobs da fila e processa cada consulta de CPF.
import { Worker } from "bullmq";
const workerCPF = new Worker(
"consulta-cpf",
async (job) => {
const { cpf, callback_url } = job.data;
console.log(
`[Job ${job.id}] Processando CPF: ${cpf} ` +
`(tentativa ${job.attemptsMade + 1})`
);
// Consultar a API de CPF
const response = await fetch(`https://api.cpfhub.io/cpf/${cpf}`, {
headers: { "x-api-key": process.env.CPFHUB_API_KEY }
});
if (!response.ok) {
throw new Error(`API retornou status ${response.status}`);
}
const resultado = await response.json();
// Atualizar progresso
await job.updateProgress(100);
// Notificar via callback se configurado
if (callback_url && resultado.success) {
await fetch(callback_url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
cpf,
dados: resultado.data,
jobId: job.id
})
});
}
return {
cpf,
sucesso: resultado.success,
dados: resultado.data || null,
processadoEm: new Date().toISOString()
};
},
{
connection: {
host: process.env.REDIS_HOST || "127.0.0.1",
port: parseInt(process.env.REDIS_PORT) || 6379
},
concurrency: 5,
limiter: {
max: 20,
duration: 1000 // Máximo 20 jobs por segundo
}
}
);
workerCPF.on("completed", (job, resultado) => {
console.log(`[Job ${job.id}] Concluído: ${resultado.cpf}`);
});
workerCPF.on("failed", (job, erro) => {
console.error(`[Job ${job.id}] Falhou: ${erro.message}`);
});
workerCPF.on("error", (erro) => {
console.error("Erro no worker:", erro);
});
Monitoramento com Bull Board
O Bull Board fornece uma interface visual para monitorar o estado das filas.
import { createBullBoard } from "@bull-board/api";
import { BullMQAdapter } from "@bull-board/api/bullMQAdapter.js";
import { ExpressAdapter } from "@bull-board/express";
const serverAdapter = new ExpressAdapter();
serverAdapter.setBasePath("/admin/filas");
createBullBoard({
queues: [new BullMQAdapter(filaCPF)],
serverAdapter
});
app.use("/admin/filas", serverAdapter.getRouter());
| Métrica da fila | Descrição | Meta |
|---|---|---|
| Jobs aguardando | Quantidade de CPFs na fila | < 100 |
| Jobs ativos | Sendo processados agora | Igual à concorrência |
| Jobs concluídos | Processados com sucesso | > 95% do total |
| Jobs falhados | Falharam após todas as tentativas | < 2% do total |
| Latência da fila | Tempo entre enfileirar e processar | < 5 segundos |
Consulta de status do job
Para permitir que o cliente consulte o status de um job enfileirado, implemente um endpoint de consulta.
app.get("/api/consultar-cpf/status/:jobId", async (req, res) => {
const job = await filaCPF.getJob(req.params.jobId);
if (!job) {
return res.status(404).json({ erro: "Job não encontrado" });
}
const estado = await job.getState();
const progresso = job.progress;
res.json({
jobId: job.id,
estado,
progresso,
dados: job.data,
resultado: job.returnvalue || null,
tentativas: job.attemptsMade,
criadoEm: new Date(job.timestamp).toISOString(),
processadoEm: job.processedOn
? new Date(job.processedOn).toISOString()
: null,
concluidoEm: job.finishedOn
? new Date(job.finishedOn).toISOString()
: null
});
});
Perguntas frequentes
Por que usar BullMQ em vez de processar consultas de CPF de forma síncrona?
Consultas síncronas travam o servidor enquanto aguardam resposta da API externa — se houver pico de requisições ou instabilidade na rede, o tempo de resposta da sua aplicação dispara. Com BullMQ, o endpoint retorna imediatamente um jobId e o processamento real acontece no worker em background, com retry automático caso a consulta falhe.
Quantos workers posso rodar em paralelo?
Não há limite fixo: cada instância do worker aceita um parâmetro concurrency e você pode subir múltiplos processos ou contêineres apontando para a mesma fila Redis. Para a CPFHub.io, observe o rate limit do seu plano — o plano Pro permite 1.000 consultas mensais, com excedente cobrado a R$0,15 por consulta sem bloqueio.
Como tratar CPFs que falham mesmo após todas as tentativas de retry?
Configure removeOnFail: false e implemente um listener no evento failed do worker para mover esses jobs para uma dead letter queue ou registrá-los em banco de dados. A ANPD recomenda que dados de identificação tratados por obrigação legal sejam rastreáveis — manter logs de falha faz parte dessa rastreabilidade.
O BullMQ garante que cada CPF seja processado exatamente uma vez?
O BullMQ usa locks no Redis para garantir que um job seja processado por apenas um worker por vez. Em caso de crash do worker durante o processamento, o job retorna à fila após o tempo de lock expirar. Para evitar duplicatas no resultado final, use um identificador único (ex: CPF + timestamp do lote) ao salvar os resultados.
Conclusão
Utilizar BullMQ para processar consultas de CPF em fila é uma abordagem robusta que desacopla o tempo de resposta da API do tempo de processamento real. Com retry automático, controle de concorrência, rate limiting e monitoramento visual, o sistema se torna resiliente e escalável. Essa arquitetura é especialmente valiosa para operações em lote, integrações com sistemas legados e cenários onde a disponibilidade da API não pode ser garantida.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a processar consultas de CPF em fila com Node.js hoje mesmo.
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.



