# Como integrar a API de CPF em uma aplicação Phoenix

> Aprenda a integrar a API de consulta de CPF em uma aplicação Phoenix com controllers, contexts e LiveView.

**Publicado:** 15/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-api-cpf-aplicacao-phoenix

---


O Phoenix é o framework web mais popular do ecossistema Elixir, e integrar a API de consulta de CPF do CPFHub.io nele é direto: crie um context dedicado, configure o Tesla como cliente HTTP e exponha um endpoint REST ou um LiveView. Em menos de uma tarde, sua aplicação Phoenix estará validando e enriquecendo dados de CPF em tempo real com latência de ~900ms por consulta.

## Introdução

O Phoenix é o framework web mais popular do ecossistema Elixir, conhecido por sua performance excepcional e escalabilidade. Integrar a API de consulta de CPF do CPFHub.io em uma aplicação Phoenix permite oferecer validação e enriquecimento de dados de forma eficiente.

---

## Configurando o projeto Phoenix

Crie uma nova aplicação Phoenix e adicione as dependências:

```elixir
# mix.exs
defp deps do
 [
 {:phoenix, "~> 1.7"},
 {:phoenix_live_view, "~> 0.20"},
 {:tesla, "~> 1.8"},
 {:hackney, "~> 1.20"},
 {:jason, "~> 1.4"}
 ]
end
```

Adicione a configuração da API no `config/config.exs`:

```elixir
# config/config.exs
config :minha_app, :cpfhub,
 base_url: "https://api.cpfhub.io",
 api_key: System.get_env("CPFHUB_API_KEY")
```

---

## Criando o context de CPF

Seguindo o padrão de contexts do Phoenix, crie um módulo dedicado à lógica de CPF:

```elixir
defmodule MinhaApp.Cpf do
 @moduledoc """
 Context para operações relacionadas a CPF.
 """

 alias MinhaApp.Cpf.ApiClient

 def consultar(cpf) do
 cpf_limpo = String.replace(cpf, ~r/\D/, "")

 with :ok <- validar_formato(cpf_limpo),
 {:ok, dados} <- ApiClient.consultar(cpf_limpo) do
 {:ok, dados}
 end
 end

 def validar_formato(cpf) when byte_size(cpf) == 11 do
 digitos = cpf
 |> String.graphemes()
 |> Enum.map(&String.to_integer/1)

 if Enum.uniq(digitos) |> length() == 1 do
 {:error, :digitos_repetidos}
 else
 :ok
 end
 end

 def validar_formato(_cpf), do: {:error, :formato_invalido}
end
```

---

## Implementando o cliente da API

Utilize Tesla com middlewares para criar um cliente robusto:

```elixir
defmodule MinhaApp.Cpf.ApiClient do
 use Tesla

 @config Application.compile_env(:minha_app, :cpfhub, [])

 plug Tesla.Middleware.BaseUrl, @config[:base_url] || "https://api.cpfhub.io"
 plug Tesla.Middleware.JSON
 plug Tesla.Middleware.Headers, [{"x-api-key", @config[:api_key] || ""}]
 plug Tesla.Middleware.Timeout, timeout: 10_000
 plug Tesla.Middleware.Retry, delay: 500, max_retries: 2

 def consultar(cpf) do
 case get("/cpf/#{cpf}") do
 {:ok, %Tesla.Env{status: 200, body: %{"success" => true, "data" => data}}} ->
 {:ok, parse_data(data)}

 {:ok, %Tesla.Env{status: 404}} ->
 {:error, :cpf_nao_encontrado}

 {:ok, %Tesla.Env{status: 401}} ->
 {:error, :chave_invalida}

 {:ok, %Tesla.Env{status: status}} ->
 {:error, {:status_inesperado, status}}

 {:error, reason} ->
 {:error, {:falha_conexao, reason}}
 end
 end

 defp parse_data(data) do
 %{
 cpf: data["cpf"],
 nome: data["name"],
 nome_upper: data["nameUpper"],
 genero: data["gender"],
 data_nascimento: data["birthDate"],
 dia: data["day"],
 mes: data["month"],
 ano: data["year"]
 }
 end
end
```

---

## Criando o controller

Exponha um endpoint REST para consulta de CPF:

```elixir
defmodule MinhaAppWeb.CpfController do
 use MinhaAppWeb, :controller

 alias MinhaApp.Cpf

 def show(conn, %{"cpf" => cpf}) do
 case Cpf.consultar(cpf) do
 {:ok, dados} ->
 conn
 |> put_status(:ok)
 |> json(%{success: true, data: dados})

 {:error, :cpf_nao_encontrado} ->
 conn
 |> put_status(:not_found)
 |> json(%{success: false, message: "CPF não encontrado"})

 {:error, :formato_invalido} ->
 conn
 |> put_status(:bad_request)
 |> json(%{success: false, message: "Formato de CPF inválido"})

 {:error, _reason} ->
 conn
 |> put_status(:bad_gateway)
 |> json(%{success: false, message: "Erro ao consultar API externa"})
 end
 end
end
```

| Rota | Método | Controller | Action |
|---|---|---|---|
| `/api/cpf/:cpf` | GET | CpfController | show |
| Resposta 200 | - | JSON com dados | Sucesso |
| Resposta 404 | - | JSON com mensagem | Não encontrado |
| Resposta 400 | - | JSON com mensagem | Formato inválido |

---

## Integrando com Phoenix LiveView

Para uma experiência interativa sem JavaScript, utilize LiveView:

```elixir
defmodule MinhaAppWeb.CpfLive do
 use MinhaAppWeb, :live_view

 alias MinhaApp.Cpf

 def mount(_params, _session, socket) do
 {:ok, assign(socket, cpf: "", resultado: nil, loading: false, erro: nil)}
 end

 def handle_event("consultar", %{"cpf" => cpf}, socket) do
 socket = assign(socket, loading: true, erro: nil, resultado: nil)

 case Cpf.consultar(cpf) do
 {:ok, dados} ->
 {:noreply, assign(socket, resultado: dados, loading: false)}

 {:error, reason} ->
 mensagem = mensagem_erro(reason)
 {:noreply, assign(socket, erro: mensagem, loading: false)}
 end
 end

 defp mensagem_erro(:cpf_nao_encontrado), do: "CPF não encontrado na base"
 defp mensagem_erro(:formato_invalido), do: "Formato de CPF inválido"
 defp mensagem_erro(:chave_invalida), do: "Chave de API inválida"
 defp mensagem_erro(_), do: "Erro ao consultar o serviço"
end
```

---

## Perguntas frequentes

### Por que usar Tesla em vez de HTTPoison para integrar a API de CPF no Phoenix?

Tesla utiliza um modelo de middleware componível que permite empilhar comportamentos — autenticação, timeout, retry e logging — sem alterar o cliente base. Para integrações com a API CPFHub.io em Phoenix, isso significa adicionar `Tesla.Middleware.Retry` para lidar com falhas transitórias e `Tesla.Middleware.Timeout` para garantir que nenhuma consulta trave o processo, sem duplicar lógica no controller. A documentação do Tesla está disponível em [hexdocs.pm/tesla](https://hexdocs.pm/tesla).

### Como estruturar o context de CPF para seguir os padrões do Phoenix?

O context `MinhaApp.Cpf` deve encapsular toda a lógica de domínio — validação de formato, chamada à API e transformação de dados — deixando o controller apenas com responsabilidades de HTTP. Isso facilita testes unitários do context sem depender de HTTP e permite substituir o client HTTP por um mock em testes.

### A API CPFHub.io retorna HTTP 429 quando o limite de consultas é atingido?

Não. A CPFHub.io não bloqueia requisições ao atingir o limite do plano: consultas excedentes são cobradas a R$ 0,15 cada. O plano gratuito oferece 50 consultas mensais sem cartão de crédito; o plano Pro inclui 1.000 consultas por R$ 149/mês com o mesmo modelo de excedente.

### Como testar a integração com a API de CPF em aplicações Phoenix?

Use `Mox` para criar um mock do `ApiClient` e isolar os testes do context de CPF. Defina um behaviour com `consultar/1` e configure o mock para retornar tuplas de sucesso ou erro. Isso garante testes rápidos e determinísticos sem depender de rede ou quota da API.

### Leia também

- [Como validar CPF em tempo real usando Elixir e APIs REST](https://cpfhub.io/blog/como-validar-cpf-tempo-real-elixir-apis-rest)
- [Como integrar validação de CPF em Elixir com Phoenix LiveView](https://cpfhub.io/blog/como-integrar-validacao-de-cpf-em-elixir-com-phoenix-liveview)
- [Como consumir a API de CPF em Elixir usando HTTPoison e Tesla](https://cpfhub.io/blog/como-consumir-api-cpf-elixir-httpoison-tesla)
- [Como criar um microsserviço de validação de CPF com Elixir e Phoenix](https://cpfhub.io/blog/como-criar-microsservico-validacao-cpf-elixir-phoenix)

---

## Conclusão

Integrar a API de CPF em uma aplicação Phoenix resulta em um sistema bem estruturado, com separação clara entre contexts, controllers e views. O LiveView adiciona interatividade sem a complexidade de JavaScript frontend.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a construir sua integração Phoenix com dados cadastrais reais em minutos.

