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ão | Descrição | Benefício |
|---|---|---|
| factory constructor | Cria instância a partir de Map | Deserialização limpa e type-safe |
| toJson() | Converte para Map | Serialização para cache ou envio |
| required named params | Parâmetros nomeados obrigatórios | Có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 teste | Método | Resultado esperado |
|---|---|---|
| CPF válido | consultarCPF('12345678909') | CPFData com nome |
| CPF curto | consultarCPF('123') | CPFServiceException |
| API retorna 401 | Mock com status 401 | Exceção de autenticação |
| Timeout | Mock com delay | Exceçã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.
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.



