Para consultar a API de CPF em Java Spring, use RestTemplate para aplicações MVC tradicionais com baixo volume de consultas, ou WebClient para projetos WebFlux e cenários de alta concorrência. O RestTemplate é síncrono e mais simples de configurar; o WebClient é reativo, não-bloqueante e processa lotes de CPFs em paralelo, reduzindo o tempo de validação de ~20 segundos para ~2 segundos em lotes de 100 documentos.
Introdução
O ecossistema Spring oferece duas opções principais para consumir APIs REST: o RestTemplate, síncrono e amplamente utilizado, e o WebClient, reativo e não-bloqueante. Ambos são capazes de consumir a API de CPF de forma eficiente, mas cada um tem características que o tornam mais adequado para cenários específicos.
Comparação entre RestTemplate e WebClient
Entender as diferenças fundamentais ajuda a escolher a ferramenta certa.
| Característica | RestTemplate | WebClient |
|---|---|---|
| Modelo | Síncrono (bloqueante) | Reativo (não-bloqueante) |
| Introdução | Spring 3.0 | Spring 5.0 (WebFlux) |
| Status | Em modo de manutenção | Recomendado para novos projetos |
| Thread por requisição | Sim | Não (event loop) |
| Adequado para | APIs simples, baixo volume | Alta concorrência, streaming |
| Curva de aprendizado | Baixa | Média (programação reativa) |
| Dependência | spring-boot-starter-web | spring-boot-starter-webflux |
Implementação com RestTemplate
O RestTemplate é direto e familiar para desenvolvedores Java.
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.time.Duration;
@Service
public class CpfRestTemplateService {
private final RestTemplate restTemplate;
private final String apiKey;
public CpfRestTemplateService(
RestTemplateBuilder builder,
@Value("${cpfhub.api-key}") String apiKey) {
this.restTemplate = builder
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.build();
this.apiKey = apiKey;
}
public CpfData consultar(String cpf) {
String cpfLimpo = cpf.replaceAll("\\D", "");
HttpHeaders headers = new HttpHeaders();
headers.set("x-api-key", apiKey);
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Void> entity = new HttpEntity<>(headers);
try {
ResponseEntity<CpfResponse> response =
restTemplate.exchange(
"https://api.cpfhub.io/cpf/" + cpfLimpo,
HttpMethod.GET,
entity,
CpfResponse.class
);
CpfResponse body = response.getBody();
if (body != null && body.isSuccess()) {
return body.getData();
}
throw new CpfNaoEncontradoException(
"CPF nao encontrado"
);
} catch (HttpClientErrorException.Unauthorized e) {
throw new ApiException("API key invalida");
} catch (HttpClientErrorException e) {
throw new ApiException(
"Erro HTTP: " + e.getStatusCode()
);
}
}
public List<CpfData> consultarLote(List<String> cpfs) {
return cpfs.stream()
.map(this::consultar)
.toList();
}
}
Implementação com WebClient
O WebClient oferece uma API fluente e suporte reativo.
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;
@Service
public class CpfWebClientService {
private final WebClient webClient;
public CpfWebClientService(
@Value("${cpfhub.api-key}") String apiKey) {
this.webClient = WebClient.builder()
.baseUrl("https://api.cpfhub.io")
.defaultHeader("x-api-key", apiKey)
.defaultHeader("Content-Type", "application/json")
.build();
}
public Mono<CpfData> consultar(String cpf) {
String cpfLimpo = cpf.replaceAll("\\D", "");
return webClient.get()
.uri("/cpf/{cpf}", cpfLimpo)
.retrieve()
.bodyToMono(CpfResponse.class)
.timeout(Duration.ofSeconds(10))
.flatMap(response -> {
if (response.isSuccess()) {
return Mono.just(response.getData());
}
return Mono.error(
new CpfNaoEncontradoException(
"CPF nao encontrado"
)
);
})
.onErrorResume(
WebClientResponseException.Unauthorized.class,
e -> Mono.error(
new ApiException("API key invalida")
)
);
}
public Flux<CpfData> consultarLote(List<String> cpfs) {
return Flux.fromIterable(cpfs)
.flatMap(this::consultar, 10) // 10 em paralelo
.onErrorContinue((erro, cpf) ->
System.err.println(
"Erro no CPF " + cpf + ": " + erro.getMessage()
)
);
}
// Versão bloqueante para uso em contextos síncronos
public CpfData consultarSync(String cpf) {
return consultar(cpf).block();
}
}
Consulta em lote: comparação de performance
A maior diferença aparece no processamento em lote, onde o WebClient brilha.
// RestTemplate: sequencial (bloqueante)
public List<CpfData> loteSincrono(List<String> cpfs) {
return cpfs.stream()
.map(this::consultar)
.toList();
}
// WebClient: paralelo (não-bloqueante)
public List<CpfData> loteReativo(List<String> cpfs) {
return Flux.fromIterable(cpfs)
.flatMap(
cpf -> consultar(cpf)
.onErrorResume(e -> Mono.empty()),
10 // concorrência máxima
)
.collectList()
.block();
}
| Métrica (100 CPFs) | RestTemplate | WebClient (10 conc.) |
|---|---|---|
| Tempo total | ~20s | ~2s |
| Threads utilizadas | 1 | ~10 (event loop) |
| Memória consumida | ~50MB | ~20MB |
| CPU utilizada | Baixa (espera I/O) | Eficiente |
| Adequado para | Lotes pequenos | Lotes grandes |
Retry e resiliência
Ambos os clientes suportam retry, mas com APIs diferentes.
// Retry com RestTemplate (usando Spring Retry)
@Retryable(
retryFor = {ApiException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public CpfData consultarComRetry(String cpf) {
return consultar(cpf);
}
// Retry com WebClient (nativo Reactor)
public Mono<CpfData> consultarComRetryReativo(String cpf) {
return consultar(cpf)
.retryWhen(
Retry.backoff(3, Duration.ofSeconds(1))
.maxBackoff(Duration.ofSeconds(10))
.filter(throwable ->
throwable instanceof ApiException
)
.doBeforeRetry(signal ->
System.out.println(
"Retentativa " + signal.totalRetries()
)
)
);
}
Quando usar cada um
A escolha depende do contexto da aplicação e do padrão de uso.
| Cenário | Recomendação | Justificativa |
|---|---|---|
| Aplicação Spring MVC tradicional | RestTemplate | Mais simples, sem necessidade de reatividade |
| Aplicação Spring WebFlux | WebClient | Nativo e não-bloqueante |
| Consultas individuais esporádicas | RestTemplate | Overhead mínimo |
| Processamento em lote de CPFs | WebClient | Concorrência eficiente |
| Migração de sistema legado | RestTemplate | Menor impacto na arquitetura |
| Novo projeto greenfield | WebClient | Recomendação oficial do Spring |
RestTemplate -- escolha quando a simplicidade é prioridade e o volume de consultas é baixo.
WebClient -- escolha quando a aplicação precisa de alta concorrência ou já usa Spring WebFlux.
Perguntas frequentes
Quando devo escolher RestTemplate em vez de WebClient para consultar a API de CPF?
Escolha RestTemplate quando a aplicação já usa Spring MVC (não WebFlux), o volume de consultas é baixo (validações individuais no cadastro, por exemplo) e a equipe não tem experiência com programação reativa. O RestTemplate é síncrono, bloqueante e mais fácil de depurar. Para novos projetos ou cenários de lote, o WebClient é a recomendação oficial do Spring desde a versão 5.
Como configurar timeout no RestTemplate e no WebClient para a API de CPF?
No RestTemplate, use RestTemplateBuilder.setConnectTimeout(Duration.ofSeconds(5)).setReadTimeout(Duration.ofSeconds(10)). No WebClient, adicione .timeout(Duration.ofSeconds(10)) na cadeia de chamada do Mono. A API da CPFHub.io tem latência de ~900ms, portanto timeouts de 10 segundos de leitura oferecem margem segura sem prejudicar a experiência do usuário.
A API da CPFHub.io bloqueia requisições quando o limite do plano é atingido?
Não. A API da CPFHub.io não retorna erros de bloqueio ao atingir o limite do plano — ela cobra R$0,15 por consulta excedente e continua respondendo normalmente. Portanto, não é necessário implementar lógica de retry específica para limite de cota. O retry deve ser configurado apenas para erros transitórios de rede ou indisponibilidade temporária.
Como processar um lote de 1.000 CPFs de forma eficiente com WebClient?
Use Flux.fromIterable(cpfs).flatMap(this::consultar, N) onde N é o nível de concorrência desejado. Com concorrência 10 e latência de ~900ms por consulta, 1.000 CPFs levam aproximadamente 90 segundos. Ajuste o N conforme os limites do seu plano e a capacidade do servidor. O projeto Reactor tem documentação detalhada sobre controle de concorrência com flatMap.
Conclusão
Tanto RestTemplate quanto WebClient são capazes de consumir a API de CPF de forma eficiente em Java Spring. O RestTemplate é mais simples e familiar, ideal para aplicações tradicionais com baixo volume. O WebClient é a escolha moderna e recomendada, especialmente para cenários de alta concorrência e processamento em lote. Independentemente da escolha, o tratamento de erros, retry e timeout devem ser configurados para garantir a resiliência da integração.
Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a API de CPF na sua aplicação Spring com RestTemplate ou WebClient em menos de 30 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.



