# Como Integrar a API de CPF em uma Aplicação Spring Boot

> Aprenda a integrar a API de CPF em uma aplicação Spring Boot com RestTemplate, service layer, validação e tratamento de erros completo.

**Publicado:** 13/09/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/integrar-api-cpf-spring-boot

---


Para integrar a API de CPF em uma aplicação Spring Boot, configure o `RestTemplate` com os timeouts e o header `x-api-key`, crie um service que encapsula a chamada para `GET https://api.cpfhub.io/cpf/{CPF}` e trate as exceções por tipo de resposta. A estrutura completa inclui propriedades externalizadas via `application.yml`, DTOs tipados e um `@RestControllerAdvice` para tratamento centralizado de erros.

## Introdução

O Spring Boot é o framework mais utilizado para desenvolvimento de aplicações Java empresariais, e integrar APIs externas é uma das tarefas mais comuns nesse ecossistema.

## Configuração do projeto

Comece configurando as dependências e propriedades da aplicação.

```java
// pom.xml (dependências relevantes)
// spring-boot-starter-web
// spring-boot-starter-validation
// jackson-databind
// lombok (opcional)

// application.yml
// cpfhub:
// api-key: ${CPFHUB_API_KEY}
// base-url: https://api.cpfhub.io
// timeout:
// connect: 5000
// read: 10000
```

```java
// CpfHubProperties.java
package com.exemplo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "cpfhub")
public class CpfHubProperties {

 private String apiKey;
 private String baseUrl = "https://api.cpfhub.io";
 private Timeout timeout = new Timeout();

 public static class Timeout {
 private int connect = 5000;
 private int read = 10000;

 // Getters e setters
 public int getConnect() { return connect; }
 public void setConnect(int connect) { this.connect = connect; }
 public int getRead() { return read; }
 public void setRead(int read) { this.read = read; }
 }

 // Getters e setters
 public String getApiKey() { return apiKey; }
 public void setApiKey(String apiKey) { this.apiKey = apiKey; }
 public String getBaseUrl() { return baseUrl; }
 public void setBaseUrl(String baseUrl) { this.baseUrl = baseUrl; }
 public Timeout getTimeout() { return timeout; }
 public void setTimeout(Timeout timeout) { this.timeout = timeout; }
}
```

| Propriedade | Valor Padrão | Descrição |
|---|---|---|
| cpfhub.api-key | (variável de ambiente) | Chave de autenticação da API |
| cpfhub.base-url | https://api.cpfhub.io | URL base da API |
| cpfhub.timeout.connect | 5000ms | Timeout de conexão |
| cpfhub.timeout.read | 10000ms | Timeout de leitura |

---

## Modelos de dados

Defina os DTOs para a comunicação com a API.

```java
// CpfResponse.java
package com.exemplo.dto;

import com.fasterxml.jackson.annotation.JsonProperty;

public class CpfResponse {

 private boolean success;
 private CpfData data;

 // Getters e setters
 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; }
}

// CpfData.java
public 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 nameUpper) { this.nameUpper = nameUpper; }
 public String getGender() { return gender; }
 public void setGender(String gender) { this.gender = gender; }
 public String getBirthDate() { return birthDate; }
 public void setBirthDate(String birthDate) { this.birthDate = birthDate; }
 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 layer

O service encapsula a lógica de chamada à API e tratamento de erros.

```java
// CpfService.java
package com.exemplo.service;

import com.exemplo.config.CpfHubProperties;
import com.exemplo.dto.CpfData;
import com.exemplo.dto.CpfResponse;
import com.exemplo.exception.CpfNaoEncontradoException;
import com.exemplo.exception.CpfHubApiException;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import java.time.Duration;

@Service
public class CpfService {

 private final RestTemplate restTemplate;
 private final CpfHubProperties properties;

 public CpfService(
 RestTemplateBuilder builder,
 CpfHubProperties properties) {
 this.properties = properties;
 this.restTemplate = builder
 .setConnectTimeout(Duration.ofMillis(
 properties.getTimeout().getConnect()))
 .setReadTimeout(Duration.ofMillis(
 properties.getTimeout().getRead()))
 .build();
 }

 public CpfData consultar(String cpf) {
 String cpfLimpo = cpf.replaceAll("\\D", "");
 validarFormatoCpf(cpfLimpo);

 HttpHeaders headers = new HttpHeaders();
 headers.set("x-api-key", properties.getApiKey());

 HttpEntity<Void> entity = new HttpEntity<>(headers);

 try {
 ResponseEntity<CpfResponse> response = restTemplate.exchange(
 properties.getBaseUrl() + "/cpf/" + cpfLimpo,
 HttpMethod.GET,
 entity,
 CpfResponse.class
 );

 CpfResponse body = response.getBody();
 if (body != null && body.isSuccess()) {
 return body.getData();
 }

 throw new CpfNaoEncontradoException(
 "CPF " + cpfLimpo + " nao encontrado"
 );
 } catch (HttpClientErrorException.Unauthorized e) {
 throw new CpfHubApiException("API key invalida");
 } catch (HttpClientErrorException e) {
 throw new CpfHubApiException(
 "Erro na API: " + e.getStatusCode()
 );
 }
 }

 private void validarFormatoCpf(String cpf) {
 if (cpf == null || !cpf.matches("\\d{11}")) {
 throw new IllegalArgumentException(
 "CPF deve conter 11 digitos"
 );
 }
 }
}
```

---

## Controller REST

O controller expõe o endpoint para consulta de CPF.

```java
// CpfController.java
package com.exemplo.controller;

import com.exemplo.dto.CpfData;
import com.exemplo.service.CpfService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

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

 private final CpfService cpfService;

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

 @GetMapping("/{cpf}")
 public ResponseEntity<Map<String, Object>> consultar(
 @PathVariable String cpf) {
 CpfData dados = cpfService.consultar(cpf);
 return ResponseEntity.ok(Map.of(
 "sucesso", true,
 "dados", dados
 ));
 }
}

// GlobalExceptionHandler.java
@RestControllerAdvice
public class GlobalExceptionHandler {

 @ExceptionHandler(CpfNaoEncontradoException.class)
 public ResponseEntity<Map<String, String>> handleCpfNaoEncontrado(
 CpfNaoEncontradoException ex) {
 return ResponseEntity.status(404).body(Map.of(
 "erro", ex.getMessage()
 ));
 }

 @ExceptionHandler(IllegalArgumentException.class)
 public ResponseEntity<Map<String, String>> handleArgumentoInvalido(
 IllegalArgumentException ex) {
 return ResponseEntity.badRequest().body(Map.of(
 "erro", ex.getMessage()
 ));
 }

 @ExceptionHandler(CpfHubApiException.class)
 public ResponseEntity<Map<String, String>> handleApiException(
 CpfHubApiException ex) {
 return ResponseEntity.status(502).body(Map.of(
 "erro", ex.getMessage()
 ));
 }
}
```

| Endpoint | Método | Descrição |
|---|---|---|
| `/api/cpf/{cpf}` | GET | Consulta um CPF via API |
| Resposta 200 | - | CPF encontrado com dados |
| Resposta 400 | - | CPF em formato inválido |
| Resposta 404 | - | CPF não encontrado |
| Resposta 502 | - | Erro na comunicação com a API |

---

## Testes

Escreva testes unitários e de integração para garantir a qualidade.

```java
// CpfServiceTest.java
@SpringBootTest
class CpfServiceTest {

 @MockBean
 private RestTemplate restTemplate;

 @Autowired
 private CpfService cpfService;

 @Test
 void deveRetornarDadosQuandoCpfEncontrado() {
 CpfResponse response = new CpfResponse();
 response.setSuccess(true);
 CpfData data = new CpfData();
 data.setCpf("12345678901");
 data.setName("Joao da Silva");
 data.setGender("M");
 response.setData(data);

 // Configurar mock...

 CpfData resultado = cpfService.consultar("12345678901");
 assertEquals("Joao da Silva", resultado.getName());
 }

 @Test
 void deveLancarExcecaoQuandoCpfInvalido() {
 assertThrows(
 IllegalArgumentException.class,
 () -> cpfService.consultar("123")
 );
 }
}
```

---

## Perguntas frequentes

### Por que usar um `@ConfigurationProperties` para as configurações da API de CPF no Spring Boot?
Externalizar as configurações via `@ConfigurationProperties` permite alterar a API key, a URL base e os timeouts sem recompilar a aplicação — basta atualizar variáveis de ambiente ou o `application.yml`. Isso é essencial para ambientes distintos (dev, staging, prod) e para rotação segura de chaves de autenticação seguindo as boas práticas da [OWASP](https://owasp.org/www-project-top-ten/).

### Qual a diferença entre `RestTemplate` e `WebClient` para consumir a API de CPF no Spring Boot?
O `RestTemplate` é síncrono e bloqueante — adequado para aplicações Spring MVC tradicionais. O `WebClient` é não bloqueante e faz parte do Spring WebFlux — indicado para aplicações reativas. Para a maioria dos cenários de validação de CPF em cadastro ou checkout, o `RestTemplate` com timeout configurável é suficiente e mais simples de testar.

### Como tratar o caso em que o CPF não é encontrado na API?
Quando `success: false` na resposta, lance uma exceção tipada (ex.: `CpfNaoEncontradoException`) e capture-a no `@RestControllerAdvice` para retornar HTTP 404 com mensagem clara ao cliente da sua API. Não trate como erro de sistema — é um resultado de negócio esperado que deve ser comunicado de forma limpa.

### A API de CPF pode bloquear minha aplicação por volume de chamadas?
Não. A API CPFHub.io não bloqueia por volume — quando o limite do plano é ultrapassado, cada consulta extra é cobrada a R$0,15 sem interrupção do serviço. Planeje o dimensionamento pelo custo, não por disponibilidade. Para volumes elevados, o plano Pro oferece 1.000 consultas mensais por R$149 com a mesma política de excedente.

### Leia também

- [Diferença entre validação de CPF e consulta de CPF: quando usar cada uma](https://cpfhub.io/blog/diferenca-entre-validacao-de-cpf-e-consulta-de-cpf-quando-usar-cada-uma)
- [API de CPF grátis para desenvolvedores: como começar em 5 minutos](https://cpfhub.io/blog/api-cpf-gratis-desenvolvedores-comecar-5-minutos)
- [Onboarding digital em fintechs: como validar CPF em menos de 30 segundos](https://cpfhub.io/blog/onboarding-digital-em-fintechs-como-validar-cpf-em-menos-de-30-segundos)
- [KYC no Brasil: quais setores são obrigados a validar CPF por lei](https://cpfhub.io/blog/kyc-no-brasil-quais-setores-sao-obrigados-a-validar-cpf-por-lei)

---

## Conclusão

Integrar a API de CPF em uma aplicação Spring Boot segue as melhores práticas do ecossistema Spring: configuração externalizada, injeção de dependência, service layer e tratamento centralizado de exceções. Essa abordagem garante que a integração seja testável, manutenível e resiliente. O uso de RestTemplate com timeout configurável e headers de autenticação torna a comunicação com a API segura e eficiente.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e integre a validação de CPF na sua aplicação Spring Boot em menos de 30 minutos.

