Como consumir API de CPF em React Native para apps mobile nativos

Aprenda a integrar a API de consulta de CPF em React Native com fetch, validação em tempo real e UX otimizada para apps mobile nativos.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como consumir API de CPF em React Native para apps mobile nativos

Para consumir a API de CPF da CPFHub.io em um app React Native, o caminho correto é nunca chamar a API diretamente do dispositivo — a chave de API ficaria exposta no bundle. O padrão recomendado é criar um backend intermediário (Node.js, por exemplo) que recebe o CPF do app, consulta https://api.cpfhub.io/cpf/{CPF} com o header x-api-key e devolve apenas os dados necessários. A documentação oficial do React Native detalha as opções de rede disponíveis, incluindo fetch e XMLHttpRequest, que funcionam tanto em iOS quanto em Android.


Arquitetura segura para apps mobile

Em aplicações mobile, a chave de API nunca deve ser incluida no código do app. A arquitetura recomendada e:

  1. O app React Native envia o CPF para seu backend.
  2. O backend consulta a API da CPFHub.io com a chave de API.
  3. O backend retorna o resultado ao app.

Essa camada intermediaria protege a chave de API e permite adicionar rate limiting, cache e logs no servidor.


Backend intermediario (Node.js/Express)

Crie um endpoint simples que recebe o CPF do app e consulta a API:

// backend/server.js
const express = require("express");
const app = express();
const PORT = process.env.PORT || 3001;

const CPFHUB_API_KEY = process.env.CPFHUB_API_KEY;

app.get("/api/validar-cpf/:cpf", async (req, res) => {
    const cpf = req.params.cpf.replace(/\D/g, "");

    if (cpf.length !== 11) {
    return res.status(400).json({ erro: "CPF deve ter 11 digitos" });
    }

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

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

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

    if (!dados.success) {
    return res.status(404).json({ erro: "CPF nao encontrado" });
    }

    res.json({
    valido: true,
    dados: {
    nome: dados.data.name,
    genero: dados.data.gender,
    dataNascimento: dados.data.birthDate,
    },
    });
    } catch (error) {
    clearTimeout(timeoutId);
    const mensagem = error.name === "AbortError"
    ? "Timeout na consulta"
    : error.message;
    res.status(500).json({ erro: mensagem });
    }
});

app.listen(PORT, () => console.log(`Backend rodando na porta ${PORT}`));

Serviço de API no React Native

Crie um módulo que encapsula as chamadas ao backend:

// src/services/cpfService.ts
import { Platform } from "react-native";

const API_BASE_URL = __DEV__
    ? Platform.OS === "android"
    ? "http://10.0.2.2:3001"
    : "http://localhost:3001"
    : "https://api.meuapp.com.br";

const TIMEOUT_MS = 15000;

interface CPFResult {
    valido: boolean;
    dados?: {
    nome: string;
    genero: string;
    dataNascimento: string;
    };
    erro?: string;
}

export async function validarCPF(cpf: string): Promise<CPFResult> {
    const cpfLimpo = cpf.replace(/\D/g, "");

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

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

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

    clearTimeout(timeoutId);

    const dados = await response.json();

    if (!response.ok) {
    return { valido: false, erro: dados.erro || "Erro na consulta" };
    }

    return dados;
    } catch (error) {
    clearTimeout(timeoutId);

    if ((error as Error).name === "AbortError") {
    return { valido: false, erro: "Tempo limite excedido. Verifique sua conexao." };
    }

    return { valido: false, erro: "Sem conexao com o servidor" };
    }
}

Note a diferenciacao de URL para Android (que usa 10.0.2.2 para acessar o localhost do host) e iOS.


Componente de validação de CPF

O componente principal com mascara de input, feedback visual e tratamento de estados:

// src/screens/ValidarCPFScreen.tsx
import React, { useState, useCallback } from "react";
import {
    View,
    Text,
    TextInput,
    TouchableOpacity,
    ActivityIndicator,
    StyleSheet,
    Alert,
    KeyboardAvoidingView,
    Platform,
} from "react-native";
import { validarCPF } from "../services/cpfService";

function formatarCPF(texto: string): string {
    const digitos = texto.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)}`;
}

export default function ValidarCPFScreen() {
    const [cpf, setCpf] = useState("");
    const [carregando, setCarregando] = useState(false);
    const [resultado, setResultado] = useState<{
    nome?: string;
    genero?: string;
    dataNascimento?: string;
    } | null>(null);
    const [erro, setErro] = useState<string | null>(null);

    const handleChangeCPF = useCallback((texto: string) => {
    setCpf(formatarCPF(texto));
    setResultado(null);
    setErro(null);
    }, []);

    const handleConsultar = useCallback(async () => {
    const cpfLimpo = cpf.replace(/\D/g, "");

    if (cpfLimpo.length !== 11) {
    setErro("Informe um CPF completo com 11 digitos.");
    return;
    }

    setCarregando(true);
    setErro(null);
    setResultado(null);

    try {
    const res = await validarCPF(cpfLimpo);

    if (res.valido && res.dados) {
    setResultado(res.dados);
    } else {
    setErro(res.erro ?? "CPF nao encontrado.");
    }
    } catch {
    Alert.alert("Erro", "Falha ao consultar CPF. Tente novamente.");
    } finally {
    setCarregando(false);
    }
    }, [cpf]);

    return (
    <KeyboardAvoidingView
    style={styles.container}
    behavior={Platform.OS === "ios" ? "padding" : "height"}
    >
    <Text style={styles.titulo}>Validar CPF</Text>

    <TextInput
    style={styles.input}
    value={cpf}
    onChangeText={handleChangeCPF}
    placeholder="000.000.000-00"
    keyboardType="number-pad"
    maxLength={14}
    returnKeyType="done"
    />

    <TouchableOpacity
    style={[styles.botao, carregando && styles.botaoDesabilitado]}
    onPress={handleConsultar}
    disabled={carregando}
    activeOpacity={0.7}
    >
    {carregando ? (
    <ActivityIndicator color="#ffffff" />
    ) : (
    <Text style={styles.botaoTexto}>Consultar</Text>
    )}
    </TouchableOpacity>

    {erro && (
    <View style={styles.erroContainer}>
    <Text style={styles.erroTexto}>{erro}</Text>
    </View>
    )}

    {resultado && (
    <View style={styles.resultadoContainer}>
    <Text style={styles.resultadoLabel}>Nome</Text>
    <Text style={styles.resultadoValor}>{resultado.nome}</Text>

    <Text style={styles.resultadoLabel}>Genero</Text>
    <Text style={styles.resultadoValor}>
    {resultado.genero === "M" ? "Masculino" : "Feminino"}
    </Text>

    <Text style={styles.resultadoLabel}>Data de nascimento</Text>
    <Text style={styles.resultadoValor}>{resultado.dataNascimento}</Text>
    </View>
    )}
    </KeyboardAvoidingView>
    );
}

const styles = StyleSheet.create({
    container: {
    flex: 1,
    padding: 24,
    backgroundColor: "#f8f9fa",
    },
    titulo: {
    fontSize: 24,
    fontWeight: "700",
    marginBottom: 24,
    color: "#1a1a2e",
    },
    input: {
    backgroundColor: "#ffffff",
    borderRadius: 8,
    padding: 16,
    fontSize: 18,
    borderWidth: 1,
    borderColor: "#dee2e6",
    marginBottom: 16,
    },
    botao: {
    backgroundColor: "#4361ee",
    borderRadius: 8,
    padding: 16,
    alignItems: "center",
    },
    botaoDesabilitado: {
    opacity: 0.6,
    },
    botaoTexto: {
    color: "#ffffff",
    fontSize: 16,
    fontWeight: "600",
    },
    erroContainer: {
    backgroundColor: "#fee2e2",
    borderRadius: 8,
    padding: 12,
    marginTop: 16,
    },
    erroTexto: {
    color: "#dc2626",
    fontSize: 14,
    },
    resultadoContainer: {
    backgroundColor: "#ffffff",
    borderRadius: 8,
    padding: 16,
    marginTop: 16,
    borderWidth: 1,
    borderColor: "#d1fae5",
    },
    resultadoLabel: {
    fontSize: 12,
    color: "#6b7280",
    marginTop: 8,
    },
    resultadoValor: {
    fontSize: 16,
    color: "#1a1a2e",
    fontWeight: "500",
    },
});

Tratamento de conectividade

Apps mobile enfrentam desafios de conectividade que aplicações web não tem. Use o módulo @react-native-community/netinfo para verificar a conexão antes de consultar:

import NetInfo from "@react-native-community/netinfo";

async function verificarConexao(): Promise<boolean> {
    const state = await NetInfo.fetch();
    return state.isConnected ?? false;
}

// Antes de consultar
if (!(await verificarConexao())) {
    setErro("Sem conexao com a internet. Verifique sua rede.");
    return;
}

Boas práticas para mobile

  • Nunca armazene a chave de API no app -- Use sempre um backend intermediario.
  • Timeout generoso -- Conexões mobile são mais lentas; use 15 segundos em vez de 10.
  • Feedback visual -- Sempre mostre indicadores de carregamento; usuários mobile são impacientes.
  • Teclado numerico -- Use keyboardType="number-pad" para facilitar a digitacao do CPF.
  • Mascara de input -- Formate o CPF automaticamente para reduzir erros.

Perguntas frequentes

Por que não posso chamar a API de CPF diretamente do app React Native?

Chamadas diretas do app exporiam a chave de API no bundle JavaScript, que pode ser inspecionado com ferramentas como react-native-decompiler ou simplesmente monitorando o tráfego de rede. Com a chave comprometida, qualquer pessoa poderia consumir sua cota ou gerar cobranças. O backend intermediário é a única forma segura de guardar credenciais em aplicações mobile.

Como o timeout de 15 segundos afeta a experiência do usuário?

Em redes móveis — especialmente 3G ou conexões instáveis — a latência pode ser significativamente maior do que em desktop. O timeout de 15 segundos no lado do app dá margem suficiente para o backend consultar a API (latência típica de ~900ms) e retornar, mesmo com alguma degradação de rede. Se estourar, exiba uma mensagem clara pedindo ao usuário para tentar novamente.

A API CPFHub.io retorna erro quando o limite de consultas é atingido?

Não. Ao ultrapassar o limite do plano — 50 consultas no gratuito ou 1.000 no Pro (R$149/mês) — a API continua respondendo normalmente e cobra R$0,15 por consulta adicional. Não há interrupção do serviço nem código de erro específico por limite excedido. O controle de custos é feito pelo painel em app.cpfhub.io/settings/billing.

Como adaptar o componente para Expo Go durante o desenvolvimento?

Em projetos Expo, a URL do emulador Android muda para http://10.0.2.2:PORT, enquanto dispositivos físicos precisam do IP da máquina na rede local. O serviço já trata isso via Platform.OS. Para produção, substitua a URL pelo domínio real do seu backend. Consulte a documentação de rede do React Native para mais detalhes.


Conclusão

A integração da API de CPF da CPFHub.io

Cadastre-se 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.

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