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

**Publicado:** 18/07/2026
**Autor:** Redação CPFHub.io
**URL:** https://cpfhub.io/blog/como-integrar-validacao-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

* **Clojure 1.11+** e **Leiningen** (ou deps.edn) instalados. Consulte o [guia de início rápido do Clojure](https://clojure.org/guides/getting_started) para instruções de instalação.

* Uma conta gratuita na [**CPFHub.io**](https://www.cpfhub.io/)

---

## 2. Configure o projeto

No `project.clj`, adicione as dependências:

```clojure
;; 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:

```clojure
;; 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

```clojure
;; 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:

```clojure
;; 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:

```clojure
;; 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

```clojure
;; 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:

```bash
lein run
```

```bash
curl -X GET http://localhost:3000/api/cpf/12345678900
```

Resposta esperada:

```json
{
 "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:

```clojure
;; 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.

---

### Leia também

- [Como validar CPF no frontend com React e API REST](https://cpfhub.io/blog/como-validar-cpf-no-frontend-com-react-e-api-rest)
- [Boas práticas para consumir APIs de CPF de forma segura](https://cpfhub.io/blog/boas-praticas-consumir-apis-cpf-segura)
- [Como consumir API de CPF em Rust com reqwest e serde](https://cpfhub.io/blog/como-consumir-api-de-cpf-em-rust-com-reqwest-e-serde)
- [Como criar um SDK interno para padronizar consultas de CPF na empresa](https://cpfhub.io/blog/como-criar-sdk-interno-padronizar-consultas-cpf-empresa)

---

## Conclusão

Integrar a API da [**CPFHub.io**](https://www.cpfhub.io/)

Cadastre-se em [cpfhub.io](https://www.cpfhub.io/)

