Como integrar validação de CPF em Clojure com Ring e Compojure

Aprenda a integrar validação de CPF em Clojure usando Ring, Compojure e clj-http para consumir a API da CPFHub.io.

Redação CPFHub.io
Redação CPFHub.io
··7 min de leitura
Como integrar validação de CPF em Clojure com Ring e Compojure

Para integrar validação de CPF em Clojure com Ring e Compojure, você usa a biblioteca clj-http para chamar GET https://api.cpfhub.io/cpf/{CPF} com o header x-api-key, deserializa o JSON com cheshire e retorna mapas imutáveis com chaves :ok ou :error para tratamento funcional de erros. A latência típica da API é de ~900ms — configure :socket-timeout e :connection-timeout acima desse valor para evitar falhas espúrias. A combinação de funções puras e desenvolvimento via REPL torna a integração e o diagnóstico de problemas especialmente ágeis no ecossistema Clojure.


1. Pré-requisitos


2. Configure o projeto

No project.clj, adicione as dependências:

;; project.clj
(defproject cpf-validator "0.1.0"
    :description "Validação de CPF via API CPFHub.io"
    :dependencies [[org.clojure/clojure "1.11.1"]
    [ring/ring-core "1.10.0"]
    [ring/ring-jetty-adapter "1.10.0"]
    [ring/ring-json "0.5.1"]
    [compojure "1.7.1"]
    [clj-http "3.12.3"]
    [cheshire "5.12.0"]
    [environ "1.2.0"]]
    :main cpf-validator.core
    :plugins [[lein-environ "1.2.0"]])

3. Configure as variáveis de ambiente

Crie o arquivo profiles.clj para desenvolvimento:

;; profiles.clj (NÃO versione este arquivo)
{:dev {:env {:cpfhub-api-key "SUA_CHAVE_DE_API"
    :cpfhub-base-url "https://api.cpfhub.io"
    :cpfhub-timeout "5000"}}}

4. Crie o módulo de configuração

;; src/cpf_validator/config.clj
(ns cpf-validator.config
    (:require [environ.core :refer [env]]))

(def cpfhub-config
    {:api-key (env :cpfhub-api-key)
    :base-url (env :cpfhub-base-url "https://api.cpfhub.io")
    :timeout (Integer/parseInt (env :cpfhub-timeout "5000"))})

5. Implemente o serviço de consulta

Crie funções puras para a lógica de consulta:

;; src/cpf_validator/cpfhub.clj
(ns cpf-validator.cpfhub
    (:require [clj-http.client :as http]
    [cheshire.core :as json]
    [clojure.string :as str]
    [cpf-validator.config :refer [cpfhub-config]]))

(defn limpar-cpf
    "Remove caracteres não numéricos do CPF."
    [cpf]
    (str/replace cpf #"\D" ""))

(defn cpf-valido?
    "Verifica se o CPF tem 11 dígitos."
    [cpf]
    (= 11 (count (limpar-cpf cpf))))

(defn- mapear-erro
    "Mapeia status HTTP para mensagem de erro."
    [status]
    (case status
    400 {:error "CPF com formato inválido" :status 400}
    401 {:error "Chave de API inválida ou ausente" :status 401}
    404 {:error "CPF não encontrado na base de dados" :status 404}
    {:error (str "Erro HTTP " status) :status status}))

(defn consultar-cpf
    "Consulta dados de um CPF na API da CPFHub.io.
    Retorna {:ok data} em caso de sucesso ou {:error msg :status code} em caso de erro."
    [cpf]
    (let [cpf-limpo (limpar-cpf cpf)]
    (if-not (= 11 (count cpf-limpo))
    {:error "CPF deve conter exatamente 11 dígitos" :status 400}
    (try
    (let [url (str (:base-url cpfhub-config) "/cpf/" cpf-limpo)
    response (http/get url
    {:headers {"x-api-key" (:api-key cpfhub-config)
    "Accept" "application/json"}
    :socket-timeout (:timeout cpfhub-config)
    :connection-timeout (:timeout cpfhub-config)
    :as :json
    :throw-exceptions false})]
    (if (= 200 (:status response))
    (let [body (:body response)]
    (if (:success body)
    {:ok (:data body)}
    {:error "Resposta inesperada da API" :status 500}))
    (mapear-erro (:status response))))
    (catch java.net.SocketTimeoutException _
    {:error "Timeout ao consultar a API da CPFHub" :status 504})
    (catch Exception e
    {:error (str "Erro de conexão: " (.getMessage e)) :status 502})))))

6. Crie as rotas com Compojure

Configure as rotas da API:

;; src/cpf_validator/routes.clj
(ns cpf-validator.routes
    (:require [compojure.core :refer [defroutes GET]]
    [compojure.route :as route]
    [ring.util.response :as response]
    [cpf-validator.cpfhub :as cpfhub]))

(defn- consulta-cpf-handler
    "Handler para consulta de CPF."
    [cpf]
    (let [resultado (cpfhub/consultar-cpf cpf)]
    (if (:ok resultado)
    (response/response {:success true :data (:ok resultado)})
    (-> (response/response {:success false :error (:error resultado)})
    (response/status (:status resultado 500))))))

(defroutes app-routes
    (GET "/api/cpf/:cpf" [cpf] (consulta-cpf-handler cpf))
    (route/not-found {:success false :error "Recurso não encontrado"}))

7. Configure o middleware e inicie o servidor

;; src/cpf_validator/core.clj
(ns cpf-validator.core
    (:require [ring.adapter.jetty :refer [run-jetty]]
    [ring.middleware.json :refer [wrap-json-response wrap-json-body]]
    [ring.middleware.params :refer [wrap-params]]
    [cpf-validator.routes :refer [app-routes]])
    (:gen-class))

(def app
    (-> app-routes
    wrap-json-response
    (wrap-json-body {:keywords? true})
    wrap-params))

(defn -main
    "Inicia o servidor HTTP."
    [& args]
    (let [port (Integer/parseInt (or (System/getenv "PORT") "3000"))]
    (println (str "Servidor iniciado na porta " port))
    (run-jetty app {:port port :join? false})))

8. Teste a integração

Inicie o servidor e faça uma requisição de teste:

lein run
curl -X GET http://localhost:3000/api/cpf/12345678900

Resposta esperada:

{
    "success": true,
    "data": {
    "cpf": "12345678900",
    "name": "João da Silva",
    "nameUpper": "JOÃO DA SILVA",
    "gender": "M",
    "birthDate": "15/06/1990",
    "day": 15,
    "month": 6,
    "year": 1990
    }
}

9. Teste no REPL

Uma das vantagens do Clojure é o desenvolvimento interativo via REPL:

;; No REPL
(require '[cpf-validator.cpfhub :as cpfhub])

;; Testar consulta
(cpfhub/consultar-cpf "12345678900")
;; => {:ok {:cpf "12345678900" :name "João da Silva" ...}}

;; Testar CPF inválido
(cpfhub/consultar-cpf "123")
;; => {:error "CPF deve conter exatamente 11 dígitos" :status 400}

;; Verificar validação
(cpfhub/cpf-valido? "123.456.789-00")
;; => true

10. Boas práticas

  • Imutabilidade -- Todas as funções retornam mapas imutáveis. Erros são representados como dados, não exceções.

  • clj-http -- Use :throw-exceptions false para tratar erros HTTP como dados em vez de exceções.

  • Timeout -- Configure :socket-timeout e :connection-timeout no clj-http para evitar requisições travadas. A latência da API CPFHub.io é de ~900ms; use 5000ms como ponto de partida.

  • environ -- Use a biblioteca environ para gerenciar configurações de forma segura via variáveis de ambiente.

  • REPL -- Aproveite o REPL para testar integrações interativamente durante o desenvolvimento.

  • LGPD -- A API da CPFHub.io é 100% compatível com a LGPD. Garanta que dados pessoais sejam tratados conforme a legislação em sua aplicação Clojure.


Perguntas frequentes

Por que usar mapas com :ok e :error em vez de exceções no Clojure?

Clojure favorece dados sobre exceções: um mapa {:error "mensagem" :status 400} pode ser inspecionado, logado, transformado e passado entre funções sem nenhuma cerimônia. Exceções quebram o fluxo funcional e são mais difíceis de testar no REPL. A biblioteca clj-http com :throw-exceptions false reforça esse padrão ao entregar respostas de erro como dados normais — o handler decide o que fazer com elas, mantendo as funções de consulta puras e testáveis.

Como o clj-http lida com a latência de ~900ms da API CPFHub.io?

O clj-http usa conexões bloqueantes por padrão. Com :socket-timeout 5000 e :connection-timeout 5000, a biblioteca aguarda até 5 segundos pela resposta antes de lançar java.net.SocketTimeoutException, que o catch na função consultar-cpf converte em {:error "Timeout..." :status 504}. Para cargas maiores, considere usar clj-http.client com um pool de conexões ou migrar para hato, que usa o HttpClient assíncrono do Java 11.

O que acontece quando o limite de consultas do plano gratuito é atingido?

A API CPFHub.io não bloqueia nem retorna erro ao ultrapassar o limite. O plano gratuito inclui 50 consultas por mês e, ao esgotá-las, cada consulta adicional é cobrada a R$0,15 automaticamente. O código Clojure não precisa tratar nenhum caso especial para isso — a resposta continua sendo um 200 OK com os dados do CPF. Para controle de custos, monitore o uso pelo painel em app.cpfhub.io.

Como testar a função consultar-cpf sem fazer chamadas reais à API?

No REPL ou em testes com clojure.test, use with-redefs para substituir clj-http.client/get por uma função que retorna um mapa fixo. Exemplo: (with-redefs [http/get (fn [_ _] {:status 200 :body {:success true :data {...}}})] (consultar-cpf "12345678900")). Essa abordagem não requer mocks de objetos nem frameworks externos — é só Clojure puro, compatível com o ciclo de desenvolvimento via REPL.



Conclusão

Integrar a API da CPFHub.io

Cadastre-se em cpfhub.io

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