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.

Redação CPFHub.io
Redação CPFHub.io
··6 min de leitura
Como criar um microsserviço de validação de CPF com Elixir e 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):

# 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
ComponenteResponsabilidade
RouterRoteamento de requisições HTTP
CpfControllerProcessamento de requisições de CPF
CpfContextLógica de negócio e validação
ApiClientComunicação com a API do CPFHub
HealthControllerEndpoint de health check

Implementando o context de CPF

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

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:

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:

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:

# 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.


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 — 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.

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