# Como criar um microsserviço de validação de CPF com Elixir e Phoenix

> Crie um microsserviço de validação de CPF completo com Elixir e Phoenix, incluindo health check, métricas e documentação.

**Publicado:** 24/11/2024
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-criar-microsservico-validacao-cpf-elixir-phoenix

---


Criar um microsserviço de validação de CPF com Elixir e Phoenix combina a robustez do runtime BEAM com a API do CPFHub.io para entregar verificações de identidade confiáveis em produção. O resultado é um serviço isolado, tolerante a falhas e escalável horizontalmente que pode ser consumido por qualquer parte da sua arquitetura. A latência de resposta da API CPFHub.io fica em torno de 900ms, e o plano gratuito cobre 50 consultas mensais sem cartão de crédito.

## Introdução

Microsserviços são ideais para encapsular funcionalidades específicas como a validação de CPF. Elixir, com Phoenix e o runtime da BEAM, oferece tolerância a falhas, concorrência massiva e hot-code reloading — características que tornam a plataforma perfeita para microsserviços em produção.

## Estrutura do microsserviço

Crie o projeto Phoenix otimizado para API (sem assets e HTML):

```elixir
# Criação do projeto
# mix phx.new cpf_service --no-html --no-assets --no-dashboard --no-mailer

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

| Componente | Responsabilidade |
|---|---|
| Router | Roteamento de requisições HTTP |
| CpfController | Processamento de requisições de CPF |
| CpfContext | Lógica de negócio e validação |
| ApiClient | Comunicação com a API do CPFHub |
| HealthController | Endpoint de health check |

---

## Implementando o context de CPF

O context encapsula toda a lógica de validação e consulta:

```elixir
defmodule CpfService.Cpf do
 alias CpfService.Cpf.{Validator, ApiClient}

 def validar(cpf) do
 cpf_limpo = limpar_cpf(cpf)

 with :ok <- Validator.validar_formato(cpf_limpo),
 :ok <- Validator.validar_digitos(cpf_limpo) do
 {:ok, %{cpf: cpf_limpo, formato_valido: true, mensagem: "CPF válido"}}
 end
 end

 def consultar(cpf) do
 cpf_limpo = limpar_cpf(cpf)

 with :ok <- Validator.validar_formato(cpf_limpo),
 :ok <- Validator.validar_digitos(cpf_limpo),
 {:ok, dados} <- ApiClient.consultar(cpf_limpo) do
 {:ok, %{
 cpf: cpf_limpo,
 nome: dados["name"],
 nome_upper: dados["nameUpper"],
 genero: dados["gender"],
 data_nascimento: dados["birthDate"],
 dia: dados["day"],
 mes: dados["month"],
 ano: dados["year"]
 }}
 end
 end

 defp limpar_cpf(cpf), do: String.replace(cpf, ~r/\D/, "")
end

defmodule CpfService.Cpf.Validator do
 def validar_formato(cpf) when byte_size(cpf) != 11, do: {:error, :formato_invalido}
 def validar_formato(_cpf), do: :ok

 def validar_digitos(cpf) do
 digitos = cpf |> String.graphemes() |> Enum.map(&String.to_integer/1)

 if Enum.uniq(digitos) |> length() == 1 do
 {:error, :digitos_repetidos}
 else
 d1 = calcular_digito(digitos, 9, 10)
 d2 = calcular_digito(digitos, 10, 11)

 if Enum.at(digitos, 9) == d1 and Enum.at(digitos, 10) == d2 do
 :ok
 else
 {:error, :digitos_invalidos}
 end
 end
 end

 defp calcular_digito(digitos, count, peso) do
 soma = digitos |> Enum.take(count) |> Enum.with_index()
 |> Enum.reduce(0, fn {d, i}, acc -> acc + d * (peso - i) end)
 resto = rem(soma, 11)
 if resto < 2, do: 0, else: 11 - resto
 end
end
```

---

## Cliente da API com Tesla

Configure o cliente HTTP com middlewares para resiliência. A documentação oficial do Tesla está disponível em [hexdocs.pm/tesla](https://hexdocs.pm/tesla/readme.html):

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

 plug Tesla.Middleware.BaseUrl, "https://api.cpfhub.io"
 plug Tesla.Middleware.JSON
 plug Tesla.Middleware.Timeout, timeout: 10_000
 plug Tesla.Middleware.Retry, delay: 500, max_retries: 2

 def consultar(cpf) do
 api_key = Application.get_env(:cpf_service, :cpfhub_api_key)
 headers = [{"x-api-key", api_key}]

 case get("/cpf/#{cpf}", headers: headers) do
 {:ok, %{status: 200, body: %{"success" => true, "data" => data}}} ->
 {:ok, data}

 {:ok, %{status: 404}} ->
 {:error, :nao_encontrado}

 {:ok, %{status: 401}} ->
 {:error, :nao_autorizado}

 {:ok, %{status: status}} ->
 {:error, {:status_inesperado, status}}

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

---

## Rotas e controllers

Configure o router e os controllers do microsserviço:

```elixir
defmodule CpfServiceWeb.Router do
 use CpfServiceWeb, :router

 pipeline :api do
 plug :accepts, ["json"]
 end

 scope "/api", CpfServiceWeb do
 pipe_through :api

 get "/cpf/validar/:cpf", CpfController, :validar
 get "/cpf/consultar/:cpf", CpfController, :consultar
 get "/health", HealthController, :check
 end
end

defmodule CpfServiceWeb.CpfController do
 use CpfServiceWeb, :controller

 alias CpfService.Cpf

 def validar(conn, %{"cpf" => cpf}) do
 case Cpf.validar(cpf) do
 {:ok, resultado} ->
 json(conn, %{success: true, data: resultado})

 {:error, reason} ->
 conn
 |> put_status(:bad_request)
 |> json(%{success: false, error: to_string(reason)})
 end
 end

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

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

 {:error, :nao_autorizado} ->
 conn |> put_status(:unauthorized) |> json(%{success: false, error: "Chave inválida"})

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

defmodule CpfServiceWeb.HealthController do
 use CpfServiceWeb, :controller

 def check(conn, _params) do
 json(conn, %{
 status: "ok",
 service: "cpf-service",
 timestamp: DateTime.utc_now() |> DateTime.to_iso8601(),
 version: Application.spec(:cpf_service, :vsn) |> to_string()
 })
 end
end
```

---

## Deploy com Docker

Prepare o microsserviço para deploy containerizado:

```elixir
# Dockerfile
# Utilize a imagem oficial do Elixir
# FROM elixir:1.16-alpine AS builder

# Configuração de release no mix.exs
def project do
 [
 app: :cpf_service,
 version: "1.0.0",
 elixir: "~> 1.16",
 releases: [
 cpf_service: [
 include_executables_for: [:unix],
 applications: [runtime_tools: :permanent]
 ]
 ]
 ]
end

# config/runtime.exs
import Config

config :cpf_service, CpfServiceWeb.Endpoint,
 url: [host: System.get_env("PHX_HOST") || "localhost"],
 http: [port: String.to_integer(System.get_env("PORT") || "4000")],
 server: true

config :cpf_service, :cpfhub_api_key, System.get_env("CPFHUB_API_KEY")
```

---

## Perguntas frequentes

### Como a API CPFHub.io se comporta quando o limite do plano gratuito é atingido?
A API nunca bloqueia requisições nem retorna HTTP 429. Quando o limite de 50 consultas mensais do plano gratuito é ultrapassado, cada consulta adicional é cobrada a R$0,15. Isso garante que o microsserviço continue funcionando sem interrupções, mesmo em meses com volume maior.

### Qual é o formato da resposta da API CPFHub.io?
A API retorna um JSON com os campos `success`, e dentro de `data`: `cpf`, `name`, `nameUpper`, `gender`, `birthDate`, `day`, `month` e `year`. A autenticação é feita pelo header `x-api-key` em chamadas `GET https://api.cpfhub.io/cpf/{CPF}`.

### Por que usar Elixir e Phoenix em vez de outras stacks para este microsserviço?
O runtime BEAM oferece isolamento de processos, supervisão automática e hot-code reloading. Uma falha em uma consulta de CPF não derruba as demais, o que é essencial em microsserviços de alta disponibilidade. A biblioteca Tesla facilita o gerenciamento de timeouts e retries sem boilerplate adicional.

### Como configurar a chave de API em produção com segurança?
Nunca inclua a API key no código-fonte. Use variáveis de ambiente (ex: `CPFHUB_API_KEY`) lidas em `config/runtime.exs` e gerenciadas por ferramentas como Kubernetes Secrets, AWS Parameter Store ou Fly.io secrets. Rotacione a chave periodicamente pelo painel em [cpfhub.io](https://www.cpfhub.io/).

### Leia também

- [Como integrar a API de CPF em uma aplicação Phoenix](https://cpfhub.io/blog/como-integrar-api-cpf-aplicacao-phoenix)
- [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 validar CPF em tempo real usando Elixir e APIs REST](https://cpfhub.io/blog/como-validar-cpf-tempo-real-elixir-apis-rest)
- [Como consumir a API de CPF em Elixir usando HTTPoison e Tesla](https://cpfhub.io/blog/como-consumir-api-cpf-elixir-httpoison-tesla)

---

## Conclusão

Um microsserviço de validação de CPF com Elixir e Phoenix oferece alta disponibilidade, tolerância a falhas e excelente performance. A arquitetura baseada em processos da BEAM garante que falhas em uma consulta não afetem as demais, e a integração com a API CPFHub.io traz dados cadastrais reais em cada verificação.

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/) — 50 consultas mensais gratuitas, sem cartão de crédito — e comece a construir seu microsserviço de validação de CPF com Elixir hoje mesmo.

