# Como integrar validação de CPF em Electron para apps desktop

> Aprenda a integrar a API de consulta de CPF em aplicações Electron desktop com comunicação segura entre main e renderer process.

**Publicado:** 26/09/2026
**Autor:** Lucas Vieira
**URL:** https://www.cpfhub.io/blog/como-integrar-validacao-cpf-electron-apps-desktop

---


Para integrar validação de CPF em um app Electron, a chamada à API deve ocorrer exclusivamente no main process — nunca no renderer — usando IPC para transportar o resultado com segurança. A [CPFHub.io](https://www.cpfhub.io/) expõe um endpoint `GET https://api.cpfhub.io/cpf/{CPF}` autenticado pelo header `x-api-key`, com latência de ~900ms. O `safeStorage` do Electron cuida do armazenamento criptografado da chave no sistema operacional host, e um cache SQLite local reduz consultas desnecessárias.

---

## Arquitetura de seguranca no Electron

O Electron possui dois processos principais:

* **Main process** -- Executa Node.js com acesso completo ao sistema. E aqui que a chave de API deve ficar e as chamadas HTTP devem acontecer.
* **Renderer process** -- Executa o frontend (como um navegador). Deve ter acesso restrito via `contextIsolation` e `preload scripts`.

A regra de ouro: nunca exponha a chave de API no renderer process. Use IPC (Inter-Process Communication) para mediar a comunicação. Consulte a [documentação oficial do Electron sobre segurança](https://www.electronjs.org/docs/latest/tutorial/security) para uma visão completa das práticas recomendadas.

---

## Configuração do main process

O main process configura a janela e registra os handlers IPC para consulta de CPF:

```javascript
// main.js
const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");

const CPFHUB_API_KEY = process.env.CPFHUB_API_KEY;
const API_URL = "https://api.cpfhub.io/cpf";
const TIMEOUT_MS = 10000;

async function consultarCPF(cpfNumero) {
 const cpfLimpo = cpfNumero.replace(/\D/g, "");

 if (cpfLimpo.length !== 11) {
 return { sucesso: false, erro: "CPF deve conter 11 digitos" };
 }

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

 try {
 const response = await fetch(`${API_URL}/${cpfLimpo}`, {
 method: "GET",
 headers: {
 "x-api-key": CPFHUB_API_KEY,
 "Accept": "application/json",
 },
 signal: controller.signal,
 });

 clearTimeout(timeoutId);

 if (!response.ok) {
 return { sucesso: false, erro: `Erro HTTP ${response.status}` };
 }

 const resultado = await response.json();

 if (!resultado.success || !resultado.data) {
 return { sucesso: false, erro: "CPF nao encontrado" };
 }

 return {
 sucesso: true,
 dados: {
 nome: resultado.data.name,
 cpf: resultado.data.cpf,
 genero: resultado.data.gender,
 dataNascimento: resultado.data.birthDate,
 dia: resultado.data.day,
 mes: resultado.data.month,
 ano: resultado.data.year,
 },
 };
 } catch (error) {
 clearTimeout(timeoutId);

 if (error.name === "AbortError") {
 return { sucesso: false, erro: "Timeout na consulta" };
 }

 return { sucesso: false, erro: error.message };
 }
}

// Registrar handler IPC
ipcMain.handle("cpf:consultar", async (_event, cpf) => {
 return await consultarCPF(cpf);
});

function createWindow() {
 const mainWindow = new BrowserWindow({
 width: 800,
 height: 600,
 webPreferences: {
 preload: path.join(__dirname, "preload.js"),
 contextIsolation: true,
 nodeIntegration: false,
 sandbox: true,
 },
 });

 mainWindow.loadFile("index.html");
}

app.whenReady().then(createWindow);

app.on("window-all-closed", () => {
 if (process.platform !== "darwin") app.quit();
});
```

---

## Preload script para IPC seguro

O preload script expoe uma API limitada ao renderer via `contextBridge`:

```javascript
// preload.js
const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("cpfAPI", {
 consultar: (cpf) => ipcRenderer.invoke("cpf:consultar", cpf),
});
```

Isso cria um objeto `window.cpfAPI` no renderer com apenas o método `consultar`, sem expor nenhuma funcionalidade do Node.js.

---

## Interface do renderer

O HTML e JavaScript do renderer utilizam a API exposta pelo preload:

```html
<!-- index.html -->
<!DOCTYPE html>
<html lang="pt-BR">
<head>
 <meta charset="UTF-8">
 <meta http-equiv="Content-Security-Policy"
 content="default-src 'self'; script-src 'self'">
 <title>Validacao de CPF</title>
 <link rel="stylesheet" href="styles.css">
</head>
<body>
 <main>
 <h2>Consulta de CPF</h2>

 <form id="cpfForm">
 <label for="cpfInput">CPF:</label>
 <input
 type="text"
 id="cpfInput"
 placeholder="000.000.000-00"
 maxlength="14"
 required
 />
 <button type="submit" id="btnConsultar">Consultar</button>
 </form>

 <div id="loading" class="hidden">Consultando...</div>
 <div id="erro" class="hidden"></div>
 <div id="resultado" class="hidden"></div>
 </main>

 <script src="renderer.js"></script>
</body>
</html>
```

```javascript
// renderer.js
const form = document.getElementById("cpfForm");
const cpfInput = document.getElementById("cpfInput");
const loadingEl = document.getElementById("loading");
const erroEl = document.getElementById("erro");
const resultadoEl = document.getElementById("resultado");
const btnConsultar = document.getElementById("btnConsultar");

function formatarCPF(valor) {
 const digitos = valor.replace(/\D/g, "").slice(0, 11);
 if (digitos.length <= 3) return digitos;
 if (digitos.length <= 6)
 return `${digitos.slice(0, 3)}.${digitos.slice(3)}`;
 if (digitos.length <= 9)
 return `${digitos.slice(0, 3)}.${digitos.slice(3, 6)}.${digitos.slice(6)}`;
 return `${digitos.slice(0, 3)}.${digitos.slice(3, 6)}.${digitos.slice(6, 9)}-${digitos.slice(9)}`;
}

cpfInput.addEventListener("input", (e) => {
 e.target.value = formatarCPF(e.target.value);
});

form.addEventListener("submit", async (e) => {
 e.preventDefault();

 const cpf = cpfInput.value;
 erroEl.classList.add("hidden");
 resultadoEl.classList.add("hidden");
 loadingEl.classList.remove("hidden");
 btnConsultar.disabled = true;

 try {
 // Chama o main process via IPC seguro
 const resultado = await window.cpfAPI.consultar(cpf);

 if (resultado.sucesso) {
 resultadoEl.innerHTML = `
 <h3>Dados encontrados</h3>
 <p><strong>Nome:</strong> ${resultado.dados.nome}</p>
 <p><strong>Genero:</strong> ${resultado.dados.genero === "M" ? "Masculino" : "Feminino"}</p>
 <p><strong>Nascimento:</strong> ${resultado.dados.dataNascimento}</p>
 `;
 resultadoEl.classList.remove("hidden");
 } else {
 erroEl.textContent = resultado.erro;
 erroEl.classList.remove("hidden");
 }
 } catch (error) {
 erroEl.textContent = "Erro inesperado na consulta.";
 erroEl.classList.remove("hidden");
 } finally {
 loadingEl.classList.add("hidden");
 btnConsultar.disabled = false;
 }
});
```

---

## Armazenamento seguro da chave de API

Em aplicações desktop, a chave de API pode ser armazenada de forma segura usando o `safeStorage` do Electron:

```javascript
// keystore.js
const { safeStorage } = require("electron");
const fs = require("fs");
const path = require("path");

const KEY_FILE = path.join(app.getPath("userData"), "api_key.enc");

function salvarChave(apiKey) {
 if (!safeStorage.isEncryptionAvailable()) {
 throw new Error("Criptografia nao disponivel neste sistema");
 }

 const encrypted = safeStorage.encryptString(apiKey);
 fs.writeFileSync(KEY_FILE, encrypted);
}

function recuperarChave() {
 if (!fs.existsSync(KEY_FILE)) return null;

 const encrypted = fs.readFileSync(KEY_FILE);
 return safeStorage.decryptString(encrypted);
}

module.exports = { salvarChave, recuperarChave };
```

O `safeStorage` utiliza o Keychain no macOS, o DPAPI no Windows e o Secret Service no Linux -- as APIs nativas de cada sistema operacional para armazenamento seguro.

---

## Cache local com SQLite

Para evitar consultas repetidas e permitir uso offline parcial, utilize SQLite via `better-sqlite3`:

```javascript
// cache.js
const Database = require("better-sqlite3");
const path = require("path");

const db = new Database(
 path.join(app.getPath("userData"), "cpf_cache.db")
);

db.exec(`
 CREATE TABLE IF NOT EXISTS cpf_cache (
 cpf TEXT PRIMARY KEY,
 dados TEXT NOT NULL,
 consultado_em TEXT NOT NULL,
 expira_em TEXT NOT NULL
 )
`);

const inserir = db.prepare(`
 INSERT OR REPLACE INTO cpf_cache (cpf, dados, consultado_em, expira_em)
 VALUES (?, ?, datetime('now'), datetime('now', '+24 hours'))
`);

const buscar = db.prepare(`
 SELECT dados FROM cpf_cache
 WHERE cpf = ? AND expira_em > datetime('now')
`);

function buscarCache(cpf) {
 const row = buscar.get(cpf);
 return row ? JSON.parse(row.dados) : null;
}

function salvarCache(cpf, dados) {
 inserir.run(cpf, JSON.stringify(dados));
}

module.exports = { buscarCache, salvarCache };
```

---

## Content Security Policy

A CSP no header do HTML e essencial para proteger a aplicação Electron:

```html
<meta http-equiv="Content-Security-Policy"
 content="default-src 'self'; script-src 'self'; connect-src 'self'">
```

Como as chamadas HTTP acontecem no main process (Node.js), o renderer não precisa de permissão para `connect-src` externo -- toda a comunicação passa pelo IPC.

---

## Empacotamento e distribuição

Use o `electron-builder` para empacotar a aplicação:

```json
{
 "build": {
 "appId": "com.minhaempresa.cpf-validator",
 "productName": "CPF Validator",
 "mac": { "target": "dmg" },
 "win": { "target": "nsis" },
 "linux": { "target": "AppImage" }
 }
}
```

```bash
npx electron-builder --mac --win --linux
```

---

## Perguntas frequentes

### Como a chave de API da CPFHub.io deve ser protegida em um app Electron?

A chave de API nunca deve ficar no renderer process nem em arquivos de configuração sem criptografia. O caminho correto é armazená-la no main process via `safeStorage` do Electron, que usa o Keychain no macOS, DPAPI no Windows e Secret Service no Linux. Alternativamente, carregue a chave por variável de ambiente no momento do empacotamento e acesse-a apenas no main.

### A API CPFHub.io funciona para todos os volumes de consulta em apps desktop?

Sim. O plano gratuito oferece 50 consultas por mês sem cartão de crédito — ideal para testes e projetos internos menores. 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 em Electron?

Use o CPF apenas para a finalidade declarada ao titular, armazene apenas o necessário no banco de dados local e implemente controle de acesso aos logs de consulta. O cache SQLite local deve ter tempo de expiração curto e não deve persistir dados além do necessário para a operação da aplicação.

### Qual a latência esperada nas consultas à CPFHub.io e como lidar com ela na interface?

A latência típica é de ~900ms. Para manter a interface responsiva, desabilite o botão de consulta durante a chamada, exiba um indicador de carregamento e configure um timeout de 10 segundos no AbortController — como mostrado nos exemplos de código acima. Consultas repetidas ao mesmo CPF devem ser atendidas pelo cache SQLite local sem latência.

### 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)
- [Como integrar validação de CPF em Power Automate para workflows corporativos](https://cpfhub.io/blog/como-integrar-validacao-cpf-power-automate-workflows-corporativos)
- [Como implementar validação de CPF em microsserviços com Docker e Kubernetes](https://cpfhub.io/blog/como-implementar-validacao-cpf-microsservicos-docker-kubernetes)

---

## Conclusão

Electron oferece uma plataforma solida para aplicações desktop que precisam validar CPF, com a seguranca do isolamento entre processos e o poder do Node.js para chamadas HTTP. A arquitetura com IPC garante que a chave de API da [**CPFHub.io**](https://www.cpfhub.io/)

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/)

