Como Consumir a API de CPF em Dart Usando http Package

Aprenda a consumir a API de consulta de CPF em Dart usando o pacote http. Guia completo com serialização, tratamento de erros e testes.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como Consumir a API de CPF em Dart Usando http Package

Consumir a API de CPF do CPFHub.io em Dart com o pacote http exige apenas três passos: modelar a resposta com classes e factory constructors, criar um serviço com tratamento de erros tipado e injetar um http.Client mockável para testes — o resultado é um serviço completo e reutilizável em qualquer projeto Dart ou Flutter.

Introdução

Dart é a linguagem por trás do Flutter e vem ganhando espaço também em aplicações server-side. O pacote http é a solução oficial e mais utilizada para requisições HTTP em Dart, oferecendo uma API simples e eficiente. Este guia mostra como integrar a API do CPFHub.io em Dart puro, construindo um serviço completo com serialização JSON, tratamento de erros e validação local, pronto para ser reutilizado em qualquer projeto Dart ou Flutter.


Modelando a resposta com classes Dart

Dart não possui um equivalente direto ao Codable do Swift ou ao @Serializable do Kotlin, mas você pode criar factories para deserialização de JSON.

class CPFResponse {
    final bool success;
    final CPFData data;

    CPFResponse({required this.success, required this.data});

    factory CPFResponse.fromJson(Map<String, dynamic> json) {
    return CPFResponse(
    success: json['success'] as bool,
    data: CPFData.fromJson(json['data'] as Map<String, dynamic>),
    );
    }
}

class CPFData {
    final String cpf;
    final String name;
    final String nameUpper;
    final String gender;
    final String birthDate;
    final String day;
    final String month;
    final String year;

    CPFData({
    required this.cpf,
    required this.name,
    required this.nameUpper,
    required this.gender,
    required this.birthDate,
    required this.day,
    required this.month,
    required this.year,
    });

    factory CPFData.fromJson(Map<String, dynamic> json) {
    return CPFData(
    cpf: json['cpf'] as String,
    name: json['name'] as String,
    nameUpper: json['nameUpper'] as String,
    gender: json['gender'] as String,
    birthDate: json['birthDate'] as String,
    day: json['day'] as String,
    month: json['month'] as String,
    year: json['year'] as String,
    );
    }

    Map<String, dynamic> toJson() => {
    'cpf': cpf, 'name': name, 'nameUpper': nameUpper,
    'gender': gender, 'birthDate': birthDate,
    'day': day, 'month': month, 'year': year,
    };
}
PadrãoDescriçãoBenefício
factory constructorCria instância a partir de MapDeserialização limpa e type-safe
toJson()Converte para MapSerialização para cache ou envio
required named paramsParâmetros nomeados obrigatóriosCódigo auto-documentado
  • factory -- construtor que pode retornar instâncias existentes ou de subtipos
  • Map<String, dynamic> -- tipo padrão do Dart para representar objetos JSON
  • final -- campos imutáveis garantem que os dados não sejam alterados após criação

Criando o serviço de consulta com http

O pacote http oferece uma API direta para requisições GET com headers customizados.

import 'dart:convert';
import 'package:http/http.dart' as http;

class CPFServiceException implements Exception {
    final String message;
    final int? statusCode;

    CPFServiceException(this.message, {this.statusCode});

    @override
    String toString() => 'CPFServiceException: $message (status: $statusCode)';
}

class CPFService {
    final String _apiKey;
    final http.Client _client;
    final String _baseUrl = 'https://api.cpfhub.io/cpf';

    CPFService({
    required String apiKey,
    http.Client? client,
    }) : _apiKey = apiKey,
    _client = client ?? http.Client();

    Future<CPFData> consultarCPF(String cpf) async {
    final cpfLimpo = cpf.replaceAll(RegExp(r'[^0-9]'), '');

    if (cpfLimpo.length != 11) {
    throw CPFServiceException('CPF deve conter 11 dígitos');
    }

    try {
    final response = await _client
    .get(
    Uri.parse('$_baseUrl/$cpfLimpo'),
    headers: {'x-api-key': _apiKey},
    )
    .timeout(const Duration(seconds: 15));

    if (response.statusCode == 200) {
    final json = jsonDecode(response.body) as Map<String, dynamic>;
    final cpfResponse = CPFResponse.fromJson(json);

    if (!cpfResponse.success) {
    throw CPFServiceException('CPF não encontrado');
    }

    return cpfResponse.data;
    } else if (response.statusCode == 401) {
    throw CPFServiceException(
    'Chave de API inválida',
    statusCode: 401,
    );
    } else {
    // A API do CPFHub.io não retorna 429 nem bloqueia requisições;
    // consultas acima do plano são cobradas a R$0,15 cada.
    throw CPFServiceException(
    'Erro na API',
    statusCode: response.statusCode,
    );
    }
    } on CPFServiceException {
    rethrow;
    } catch (e) {
    throw CPFServiceException('Erro de conexão: $e');
    }
    }

    void dispose() {
    _client.close();
    }
}
  • http.Client -- injetável no construtor para facilitar testes com mock
  • timeout -- evita que a requisição fique pendurada indefinidamente
  • rethrow -- relança exceções do tipo correto sem perder o stack trace

Validação local do CPF

Validar o CPF localmente antes de chamar a API economiza requisições e dá feedback mais rápido.

class CPFValidator {
    static bool validar(String cpf) {
    final numeros = cpf.replaceAll(RegExp(r'[^0-9]'), '');

    if (numeros.length != 11) return false;

    // Rejeitar sequências repetidas
    if (RegExp(r'^(\d)\1{10}$').hasMatch(numeros)) return false;

    final digits = numeros.split('').map(int.parse).toList();

    // Primeiro dígito verificador
    int soma1 = 0;
    for (int i = 0; i < 9; i++) {
    soma1 += digits[i] * (10 - i);
    }
    final check1 = (soma1 % 11 < 2) ? 0 : 11 - (soma1 % 11);
    if (digits[9] != check1) return false;

    // Segundo dígito verificador
    int soma2 = 0;
    for (int i = 0; i < 10; i++) {
    soma2 += digits[i] * (11 - i);
    }
    final check2 = (soma2 % 11 < 2) ? 0 : 11 - (soma2 % 11);
    if (digits[10] != check2) return false;

    return true;
    }

    static String formatar(String cpf) {
    final n = cpf.replaceAll(RegExp(r'[^0-9]'), '');
    if (n.length != 11) return cpf;
    return '${n.substring(0, 3)}.${n.substring(3, 6)}'
    '.${n.substring(6, 9)}-${n.substring(9, 11)}';
    }
}

// Uso combinado
Future<void> validarEConsultar(String cpf) async {
    if (!CPFValidator.validar(cpf)) {
    print('CPF inválido localmente');
    return;
    }

    final service = CPFService(apiKey: 'sua-chave-aqui');
    try {
    final dados = await service.consultarCPF(cpf);
    print('Nome: ${dados.name}');
    print('CPF: ${CPFValidator.formatar(dados.cpf)}');
    print('Nascimento: ${dados.birthDate}');
    } on CPFServiceException catch (e) {
    print('Erro: ${e.message}');
    } finally {
    service.dispose();
    }
}
  • RegExp -- expressões regulares nativas do Dart para validação de padrões
  • static -- métodos estáticos que não precisam de instância para serem chamados
  • on catch -- captura apenas exceções de tipo específico para tratamento diferenciado

Testando o serviço com MockClient

O pacote http fornece MockClient para testes sem chamadas reais à rede.

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:test/test.dart';

void main() {
    group('CPFService', () {
    test('deve retornar dados quando CPF é válido', () async {
    final mockClient = MockClient((request) async {
    expect(request.url.path, contains('/cpf/12345678909'));
    expect(request.headers['x-api-key'], equals('test-key'));

    return http.Response(
    jsonEncode({
    'success': true,
    'data': {
    'cpf': '12345678909',
    'name': 'João da Silva',
    'nameUpper': 'JOÃO DA SILVA',
    'gender': 'M',
    'birthDate': '01/01/1990',
    'day': '01',
    'month': '01',
    'year': '1990',
    }
    }),
    200,
    );
    });

    final service = CPFService(apiKey: 'test-key', client: mockClient);
    final result = await service.consultarCPF('12345678909');

    expect(result.name, equals('João da Silva'));
    expect(result.gender, equals('M'));
    });

    test('deve lançar exceção para CPF inválido', () async {
    final service = CPFService(apiKey: 'test-key');
    expect(
    () => service.consultarCPF('123'),
    throwsA(isA<CPFServiceException>()),
    );
    });
    });
}
Cenário de testeMétodoResultado esperado
CPF válidoconsultarCPF('12345678909')CPFData com nome
CPF curtoconsultarCPF('123')CPFServiceException
API retorna 401Mock com status 401Exceção de autenticação
TimeoutMock com delayExceção de conexão
  • MockClient -- substitui chamadas HTTP reais por respostas controladas
  • Injeção de dependência -- o client é injetado no construtor, facilitando a troca por mock
  • group/test -- estrutura padrão de testes do Dart com descrições em português

Perguntas frequentes

Por que usar o pacote http em vez do dio para consumir a API de CPF em Dart?

O pacote http é a biblioteca oficial do time do Dart, mantida pela equipe do dart.dev, e cobre a maioria dos casos de uso com uma API mínima e bem testada. Para projetos Flutter ou Dart que não precisam de interceptors complexos, retries automáticos ou cancelamento avançado, http é a escolha mais simples e com menos dependências transitivas. O dio faz sentido quando você precisa desses recursos nativos sem implementar do zero.

Como adicionar o pacote http ao projeto Dart ou Flutter?

Adicione http: ^1.2.0 (ou versão mais recente) ao bloco dependencies do seu pubspec.yaml e execute dart pub get ou flutter pub get. Importe com import 'package:http/http.dart' as http; para evitar conflitos de namespace com as classes nativas do Dart.

A API do CPFHub.io retorna HTTP 429 quando o limite de consultas é atingido?

Não. A API do CPFHub.io não bloqueia requisições ao atingir o limite do plano — ela cobra R$0,15 por consulta adicional. O plano gratuito oferece 50 consultas/mês sem cartão de crédito, e o plano Pro inclui 1.000 consultas mensais por R$149. Não é necessário tratar HTTP 429 no seu código; erros de autenticação (401) e CPF não encontrado são os casos relevantes a cobrir.

Como reutilizar o CPFService em um app Flutter com gerenciamento de estado?

O CPFService foi projetado com injeção de dependência via construtor, o que facilita o registro em qualquer container de DI. Com o pacote provider, registre-o como ProxyProvider para que receba a chave de API de outro provider. Com riverpod, crie um Provider<CPFService> e injete-o em AsyncNotifier ou FutureProvider. O método dispose() deve ser chamado no dispose() do ViewModel ou do Notifier.


Conclusão

Consumir a API de CPF em Dart com o pacote http é direto e eficiente. A combinação de classes com factory constructors para deserialização, tratamento robusto de erros com exceções tipadas e validação local do CPF resulta em um serviço completo e testável. O design com injeção de dependência permite trocar facilmente o cliente HTTP por um mock nos testes, garantindo cobertura sem dependência de rede.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a consulta de CPF ao seu app Flutter ou projeto Dart server-side em 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