Como integrar validação de CPF em aplicações Spring WebFlux (reativo)

Aprenda a integrar a validação de CPF via API em aplicações Spring WebFlux usando WebClient reativo com exemplos completos de código.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como integrar validação de CPF em aplicações Spring WebFlux (reativo)

Integrar validação de CPF em aplicações Spring WebFlux significa usar o WebClient reativo para chamar a API CPFHub.io de forma não bloqueante: a thread do servidor fica livre enquanto aguarda a resposta, mantendo alta eficiência mesmo sob grandes volumes de requisições simultâneas.

Introdução

O Spring WebFlux é o módulo reativo do Spring Framework, projetado para construir aplicações assíncronas e não bloqueantes que podem lidar com alto volume de requisições simultâneas. Para empresas que processam milhares de cadastros, onboardings ou transações por minuto, a abordagem reativa é ideal para manter a eficiência sem desperdiçar recursos do servidor.

A validação de CPF via API é uma operação de I/O (rede) que se beneficia diretamente do modelo reativo: enquanto a resposta da API externa é aguardada, a thread do servidor fica livre para processar outras requisições.


Pré-requisitos

  • Java 17+ — Versão LTS recomendada.
  • Spring Boot 3.x com WebFlux — Dependência spring-boot-starter-webflux.
  • Conta na CPFHub.io — Para obter a chave de API (50 consultas/mês no plano gratuito).

Dependências Maven

<dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

Modelos de dados

import com.fasterxml.jackson.annotation.JsonProperty;

public record CpfResponse(
    @JsonProperty("success") boolean success,
    @JsonProperty("data") CpfData data
) {}

public record CpfData(
    @JsonProperty("cpf") String cpf,
    @JsonProperty("name") String name,
    @JsonProperty("nameUpper") String nameUpper,
    @JsonProperty("gender") String gender,
    @JsonProperty("birthDate") String birthDate,
    @JsonProperty("day") int day,
    @JsonProperty("month") int month,
    @JsonProperty("year") int year
) {}

Resposta esperada da API:

{
    "success": true,
    "data": {
    "cpf": "12345678900",
    "name": "João da Silva",
    "nameUpper": "JOÃO DA SILVA",
    "gender": "M",
    "birthDate": "15/06/1990",
    "day": 15,
    "month": 6,
    "year": 1990
    }
}

Configurando o WebClient

Crie um Bean de WebClient com timeout e headers padrão:

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

import java.time.Duration;
import java.util.concurrent.TimeUnit;

@Configuration
public class WebClientConfig {

    @Value("${cpfhub.api-key}")
    private String apiKey;

    @Value("${cpfhub.base-url:https://api.cpfhub.io}")
    private String baseUrl;

    @Bean
    public WebClient cpfHubWebClient() {
    HttpClient httpClient = HttpClient.create()
    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
    .responseTimeout(Duration.ofSeconds(10))
    .doOnConnected(conn -> conn
    .addHandlerLast(new ReadTimeoutHandler(10, TimeUnit.SECONDS))
    .addHandlerLast(new WriteTimeoutHandler(10, TimeUnit.SECONDS))
    );

    return WebClient.builder()
    .baseUrl(baseUrl)
    .clientConnector(new ReactorClientHttpConnector(httpClient))
    .defaultHeader("x-api-key", apiKey)
    .defaultHeader(HttpHeaders.ACCEPT,
    MediaType.APPLICATION_JSON_VALUE)
    .build();
    }
}

Criando o serviço reativo

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Service
public class CpfValidationService {

    private static final Logger log = LoggerFactory.getLogger(
    CpfValidationService.class);

    private final WebClient webClient;

    public CpfValidationService(WebClient cpfHubWebClient) {
    this.webClient = cpfHubWebClient;
    }

    public Mono<CpfData> validarCpf(String cpf) {
    String cpfLimpo = cpf.replaceAll("\\D", "");

    if (cpfLimpo.length() != 11) {
    return Mono.error(new IllegalArgumentException(
    "CPF inválido. Informe 11 dígitos."));
    }

    return webClient.get()
    .uri("/cpf/{cpf}", cpfLimpo)
    .retrieve()
    .onStatus(
    HttpStatusCode::isError,
    response -> Mono.error(new RuntimeException(
    "Erro HTTP: " + response.statusCode()))
    )
    .bodyToMono(CpfResponse.class)
    .flatMap(response -> {
    if (response.success() && response.data() != null) {
    log.info("CPF {} validado com sucesso.", cpfLimpo);
    return Mono.just(response.data());
    }
    log.warn("CPF {} não encontrado.", cpfLimpo);
    return Mono.empty();
    })
    .timeout(Duration.ofSeconds(10))
    .doOnError(e -> log.error(
    "Erro ao consultar CPF {}: {}", cpfLimpo, e.getMessage()));
    }
}

Observe que toda a cadeia é reativa: nenhuma thread é bloqueada enquanto aguarda a resposta da API.


Criando o Controller reativo

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.util.Map;

@RestController
@RequestMapping("/api/cpf")
public class CpfController {

    private final CpfValidationService cpfService;

    public CpfController(CpfValidationService cpfService) {
    this.cpfService = cpfService;
    }

    @GetMapping("/{cpf}")
    public Mono<ResponseEntity<Map<String, Object>>> validar(
    @PathVariable String cpf) {

    return cpfService.validarCpf(cpf)
    .map(dados -> ResponseEntity.ok(Map.<String, Object>of(
    "valido", true,
    "dados", dados
    )))
    .defaultIfEmpty(ResponseEntity.status(HttpStatus.NOT_FOUND)
    .body(Map.of(
    "valido", false,
    "erro", "CPF não encontrado."
    )))
    .onErrorResume(IllegalArgumentException.class, e ->
    Mono.just(ResponseEntity.badRequest().body(Map.of(
    "erro", e.getMessage()
    )))
    )
    .onErrorResume(e ->
    Mono.just(ResponseEntity.status(
    HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of(
    "erro", e.getMessage()
    )))
    );
    }
}

Implementando retry com backoff

Para maior resiliência, adicione retry com backoff exponencial ao serviço:

import java.time.Duration;
import reactor.util.retry.Retry;

public Mono<CpfData> validarCpfComRetry(String cpf) {
    return validarCpf(cpf)
    .retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
    .filter(throwable ->
    !(throwable instanceof IllegalArgumentException))
    .doBeforeRetry(signal ->
    log.warn("Tentativa {} de consulta de CPF.",
    signal.totalRetries() + 1))
    );
}

Testando a aplicação

curl -X GET http://localhost:8080/api/cpf/12345678900 \
    -H "Accept: application/json" \
    --max-time 10

application.properties

cpfhub.api-key=SUA_CHAVE_DE_API
cpfhub.base-url=https://api.cpfhub.io

WebFlux vs. RestTemplate

CaracterísticaRestTemplateWebClient (WebFlux)
ModeloBloqueanteReativo (não bloqueante)
ThreadsUma thread por requisiçãoPoucas threads para muitas requisições
Uso de recursosAlto em alto volumeEficiente em alto volume
StatusEm manutençãoRecomendado pelo Spring
ComplexidadeMenorMaior (exige conhecimento de Reactor)

Para aplicações com alto throughput, o WebFlux é a escolha recomendada. A documentação oficial do Spring detalha os casos de uso e as diferenças de modelo entre os dois módulos.


Boas práticas

  • Timeout — Configure timeout no Netty HttpClient e no operador .timeout().

  • Retry — Use retryWhen com backoff para lidar com falhas transitórias.

  • Não bloqueie — Nunca use .block() em controllers WebFlux. Retorne sempre Mono ou Flux.

  • Plano gratuito — O plano gratuito da CPFHub.io oferece 50 consultas/mês sem cartão de crédito. O plano Pro oferece 1.000 consultas por R$149/mês. Ao superar o limite, a API não bloqueia: cobra R$0,15 por consulta adicional.


Perguntas frequentes

Por que usar WebClient em vez de RestTemplate para chamar a API de CPF?

O WebClient é a abordagem reativa: a thread não fica bloqueada aguardando a resposta da API, liberando recursos para outras requisições. Em aplicações com alto volume de validações simultâneas (onboarding em massa, por exemplo), o ganho de eficiência é significativo. O RestTemplate está em modo de manutenção no Spring e não é recomendado para novos projetos.

Como evitar que o .block() cause deadlock em aplicações WebFlux?

Nunca chame .block() dentro de um contexto reativo — controllers, services ou handlers WebFlux. Se precisar de um valor síncrono, reestruture o código para retornar Mono ou Flux até o ponto de saída. Para testes, use StepVerifier da biblioteca Reactor Test em vez de .block().

Qual timeout configurar para a chamada à API de CPF no WebClient?

Configure pelo menos dois timeouts: connectTimeout (5 segundos) no HttpClient do Netty e responseTimeout (10 segundos) no operador .timeout() do Reactor. A API CPFHub.io tem latência típica de ~900ms; o timeout de 10 segundos é conservador o suficiente para absorver picos sem prejudicar a experiência do usuário.

Como implementar cache reativo para evitar consultas repetidas ao mesmo CPF?

Use Mono.cache() ou integre com Spring Cache via anotação @Cacheable em conjunto com um ReactiveRedisTemplate. O cache reativo evita chamadas desnecessárias para o mesmo CPF em janelas curtas de tempo, reduzindo o consumo mensal de consultas sem comprometer a qualidade dos dados.


Conclusão

Integrar a validação de CPF em aplicações Spring WebFlux usando WebClient é a abordagem ideal para sistemas que precisam lidar com alto volume de requisições de forma eficiente. Com o modelo reativo, cada consulta à API CPFHub.io ocorre sem bloquear threads, resultando em menor consumo de memória e maior throughput sob carga.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e implemente a validação reativa de CPF no seu projeto Spring WebFlux ainda hoje.

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