Como Integrar a API de CPF em uma Aplicação Quarkus

Aprenda a integrar a API de CPF em uma aplicação Quarkus com REST Client, CDI, fault tolerance e compilação nativa com GraalVM.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como Integrar a API de CPF em uma Aplicação Quarkus

Para integrar a API de CPF da CPFHub.io em uma aplicação Quarkus, utilize o REST Client declarativo com a interface anotada @RegisterRestClient, adicione fault tolerance via MicroProfile e compile nativamente com GraalVM para startup em milissegundos. O processo leva menos de uma hora e entrega um microsserviço de validação de CPF pronto para ambientes containerizados e serverless.

Introdução

O Quarkus é um framework Java otimizado para containers e GraalVM, oferecendo tempo de startup em milissegundos e consumo mínimo de memória. Para microsserviços de validação de CPF, essas características são especialmente valiosas: o serviço inicia instantaneamente, escala rapidamente e consome poucos recursos.


Configuração do projeto

Configure as dependências e propriedades do Quarkus.

// pom.xml (extensões Quarkus relevantes)
// quarkus-resteasy-reactive-jackson
// quarkus-rest-client-reactive-jackson
// quarkus-smallrye-fault-tolerance
// quarkus-smallrye-health
// quarkus-smallrye-metrics
// quarkus-container-image-docker

// application.properties
// quarkus.rest-client.cpfhub-api.url=https://api.cpfhub.io
// quarkus.rest-client.cpfhub-api.scope=jakarta.inject.Singleton
// cpfhub.api-key=${CPFHUB_API_KEY}
// quarkus.http.port=8080
ExtensãoPropósito
resteasy-reactive-jacksonEndpoints REST reativos com JSON
rest-client-reactive-jacksonCliente REST declarativo reativo
smallrye-fault-toleranceCircuit breaker, retry, timeout
smallrye-healthHealth checks (liveness, readiness)
smallrye-metricsMétricas MicroProfile
container-image-dockerBuild de imagem Docker

REST client declarativo

O Quarkus permite definir clientes REST com interfaces anotadas, eliminando código boilerplate.

// CpfHubApiClient.java
package com.cpfservice.client;

import io.quarkus.rest.client.reactive.ClientHeaderParam;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;

@Path("/cpf")
@RegisterRestClient(configKey = "cpfhub-api")
@ClientHeaderParam(
    name = "x-api-key",
    value = "${cpfhub.api-key}"
)
public interface CpfHubApiClient {

    @GET
    @Path("/{cpf}")
    CpfApiResponse consultar(@PathParam("cpf") String cpf);
}

// CpfApiResponse.java
package com.cpfservice.client;

import com.fasterxml.jackson.annotation.JsonProperty;

public class CpfApiResponse {

    private boolean success;
    private CpfData data;

    public boolean isSuccess() { return success; }
    public void setSuccess(boolean success) { this.success = success; }
    public CpfData getData() { return data; }
    public void setData(CpfData data) { this.data = data; }

    public static class CpfData {
    private String cpf;
    private String name;
    private String nameUpper;
    private String gender;
    private String birthDate;
    private int day;
    private int month;
    private int year;

    // Getters e setters
    public String getCpf() { return cpf; }
    public void setCpf(String cpf) { this.cpf = cpf; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getNameUpper() { return nameUpper; }
    public void setNameUpper(String n) { this.nameUpper = n; }
    public String getGender() { return gender; }
    public void setGender(String gender) { this.gender = gender; }
    public String getBirthDate() { return birthDate; }
    public void setBirthDate(String d) { this.birthDate = d; }
    public int getDay() { return day; }
    public void setDay(int day) { this.day = day; }
    public int getMonth() { return month; }
    public void setMonth(int month) { this.month = month; }
    public int getYear() { return year; }
    public void setYear(int year) { this.year = year; }
    }
}

Service com fault tolerance

O service utiliza anotações do MicroProfile Fault Tolerance para resiliência.

package com.cpfservice.service;

import com.cpfservice.client.CpfApiResponse;
import com.cpfservice.client.CpfHubApiClient;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.eclipse.microprofile.rest.client.inject.RestClient;

import java.time.temporal.ChronoUnit;
import java.util.Map;

@ApplicationScoped
public class CpfValidationService {

    @Inject
    @RestClient
    CpfHubApiClient apiClient;

    @Timeout(value = 10, unit = ChronoUnit.SECONDS)
    @Retry(maxRetries = 3, delay = 1, delayUnit = ChronoUnit.SECONDS)
    @CircuitBreaker(
    requestVolumeThreshold = 10,
    failureRatio = 0.5,
    delay = 30,
    delayUnit = ChronoUnit.SECONDS
    )
    @Fallback(fallbackMethod = "consultarFallback")
    public Map<String, Object> consultar(String cpf) {
    String cpfLimpo = cpf.replaceAll("\\D", "");

    if (!validarAlgoritmo(cpfLimpo)) {
    return Map.of(
    "sucesso", false,
    "fonte", "algoritmo",
    "erro", "CPF invalido"
    );
    }

    CpfApiResponse response = apiClient.consultar(cpfLimpo);

    if (response.isSuccess()) {
    CpfApiResponse.CpfData data = response.getData();
    return Map.of(
    "sucesso", true,
    "fonte", "api",
    "dados", Map.of(
    "cpf", data.getCpf(),
    "name", data.getName(),
    "nameUpper", data.getNameUpper(),
    "gender", data.getGender(),
    "birthDate", data.getBirthDate(),
    "day", data.getDay(),
    "month", data.getMonth(),
    "year", data.getYear()
    )
    );
    }

    return Map.of(
    "sucesso", false,
    "fonte", "api",
    "erro", "CPF nao encontrado"
    );
    }

    public Map<String, Object> consultarFallback(String cpf) {
    String cpfLimpo = cpf.replaceAll("\\D", "");
    boolean valido = validarAlgoritmo(cpfLimpo);
    return Map.of(
    "sucesso", valido,
    "fonte", "fallback",
    "mensagem", "API indisponivel. Validacao algoritmica: "
    + (valido ? "valido" : "invalido")
    );
    }

    private boolean validarAlgoritmo(String cpf) {
    if (cpf.length() != 11) return false;
    if (cpf.chars().distinct().count() == 1) return false;

    int soma = 0;
    for (int i = 0; i < 9; i++) {
    soma += Character.getNumericValue(cpf.charAt(i))
    * (10 - i);
    }
    int resto = (soma * 10) % 11;
    if (resto == 10) resto = 0;
    if (resto != Character.getNumericValue(cpf.charAt(9)))
    return false;

    soma = 0;
    for (int i = 0; i < 10; i++) {
    soma += Character.getNumericValue(cpf.charAt(i))
    * (11 - i);
    }
    resto = (soma * 10) % 11;
    if (resto == 10) resto = 0;
    return resto == Character.getNumericValue(cpf.charAt(10));
    }
}
AnotaçãoConfiguraçãoComportamento
@Timeout10 segundosCancela requisição lenta
@Retry3 tentativas, 1s delayRetenta em caso de falha
@CircuitBreaker50% falha em 10 req, 30s delayAbre circuito
@FallbackconsultarFallbackMétodo alternativo quando tudo falha

Endpoint REST reativo

O controller usa RESTEasy Reactive para endpoints non-blocking.

package com.cpfservice.resource;

import com.cpfservice.service.CpfValidationService;
import jakarta.inject.Inject;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;

import java.util.List;
import java.util.Map;

@Path("/api/v1/cpf")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class CpfResource {

    @Inject
    CpfValidationService service;

    @GET
    @Path("/{cpf}")
    public Response validar(@PathParam("cpf") String cpf) {
    Map<String, Object> resultado = service.consultar(cpf);
    boolean sucesso = (boolean) resultado.get("sucesso");
    int status = sucesso ? 200 : 404;
    return Response.status(status).entity(resultado).build();
    }

    @POST
    @Path("/lote")
    public Response validarLote(List<String> cpfs) {
    List<Map<String, Object>> resultados = cpfs.stream()
    .map(service::consultar)
    .toList();

    long validos = resultados.stream()
    .filter(r -> (boolean) r.get("sucesso"))
    .count();

    return Response.ok(Map.of(
    "total", cpfs.size(),
    "validos", validos,
    "invalidos", cpfs.size() - validos,
    "resultados", resultados
    )).build();
    }
}

Compilação nativa com GraalVM

O Quarkus permite compilar a aplicação em binário nativo para startup instantâneo. A documentação oficial do Quarkus detalha os requisitos de configuração para compilação nativa com GraalVM.

# Dockerfile para imagem nativa
FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 AS builder
WORKDIR /app
COPY . .
RUN ./mvnw package -Dnative -DskipTests

FROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /app
COPY --from=builder /app/target/*-runner /app/application

EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
MétricaJVM ModeNative Mode
Tempo de startup~1.5s~0.02s
Memória RSS~150MB~30MB
Tamanho da imagem~200MB~80MB
ThroughputAltoAlto
Tempo de build~30s~5min

JVM mode -- desenvolvimento e ambientes onde o tempo de build é prioridade.

Native mode -- produção com serverless, scaling rápido e economia de recursos.


Perguntas frequentes

O Quarkus REST Client declarativo suporta autenticação via header customizado?

Sim. A anotação @ClientHeaderParam permite definir headers fixos ou dinâmicos diretamente na interface do cliente. Para a API da CPFHub.io, basta declarar @ClientHeaderParam(name = "x-api-key", value = "${cpfhub.api-key}") na interface, apontando para a propriedade configurada no application.properties. O valor é injetado em tempo de compilação, sem expor a chave no código-fonte.

Como o MicroProfile Fault Tolerance lida com falhas temporárias na API de CPF?

A combinação de @Retry (3 tentativas com 1s de intervalo) e @CircuitBreaker (abre após 50% de falhas em 10 requisições) protege a aplicação contra falhas transitórias e sobrecarga. Quando o circuit breaker está aberto, o método consultarFallback assume, retornando uma validação algorítmica local para garantir disponibilidade mesmo sem acesso à API.

A compilação nativa com GraalVM é compatível com o REST Client reativo do Quarkus?

Sim, com configuração adequada. O Quarkus gera automaticamente os metadados de reflexão necessários para o GraalVM ao usar as extensões oficiais. Para classes de resposta personalizadas como CpfApiResponse, anote-as com @RegisterForReflection caso o build nativo apresente erros de desserialização em tempo de execução.

Qual é a latência esperada para consultas de CPF em modo nativo?

A latência da consulta depende principalmente do tempo de resposta da API da CPFHub.io, que é de aproximadamente 900ms. O overhead do Quarkus em modo nativo é mínimo (< 1ms por requisição), então o tempo total de uma consulta completa fica em torno de 900ms a 1.1s dependendo da rede. O timeout recomendado para o REST Client é de 10 segundos.


Conclusão

Integrar a API de CPF em uma aplicação Quarkus oferece o melhor da plataforma Java com as vantagens de frameworks cloud-native. O REST Client declarativo elimina boilerplate, o MicroProfile Fault Tolerance adiciona resiliência com anotações simples, e a compilação nativa com GraalVM entrega startup em milissegundos e consumo mínimo de memória. Essa combinação torna o Quarkus uma escolha excelente para microsserviços de validação de CPF em ambientes containerizados e serverless.

Cadastre-se em cpfhub.io — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a validação de CPF na sua aplicação Quarkus hoje mesmo.

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