O F# é uma linguagem funcional-first que roda no ecossistema .NET, com discriminated unions e composição de funções que tornam o tratamento de erros seguro e expressivo. Para integrar validação de CPF em F#, a API da CPFHub.io aceita GET https://api.cpfhub.io/cpf/{CPF} autenticado pelo header x-api-key e responde em ~150 ms com nome, data de nascimento e status do documento. O Giraffe funciona como middleware sobre ASP.NET Core para exposição da rota; o Suave oferece uma alternativa funcional mais leve e independente. Consulte a documentação oficial do F# para configurar o .NET SDK antes de começar.
1. Pré-requisitos
-
.NET 8.0+ SDK instalado.
-
Um projeto F# criado:
dotnet new console -lang F# -n CpfValidatorFSharp. -
Uma conta gratuita na CPFHub.io
2. Adicione as dependências
dotnet add package Giraffe
dotnet add package FSharp.Data
dotnet add package Newtonsoft.Json
3. Defina os tipos de dados
Use discriminated unions para modelar resultados e erros:
// Types.fs
module CpfValidator.Types
open System.Text.Json.Serialization
type CpfData =
{ [<JsonPropertyName("cpf")>] Cpf: string
[<JsonPropertyName("name")>] Name: string
[<JsonPropertyName("nameUpper")>] NameUpper: string
[<JsonPropertyName("gender")>] Gender: string
[<JsonPropertyName("birthDate")>] BirthDate: string
[<JsonPropertyName("day")>] Day: int
[<JsonPropertyName("month")>] Month: int
[<JsonPropertyName("year")>] Year: int }
type CpfApiResponse =
{ [<JsonPropertyName("success")>] Success: bool
[<JsonPropertyName("data")>] Data: CpfData option }
type CpfError =
| InvalidCpf of string
| NotFound of string
| Unauthorized of string
| RateLimited of string
| TimeoutError of string
| ApiError of string * int
type CpfResult = Result<CpfData, CpfError>
4. Implemente o cliente da API
Crie o módulo de consulta usando HttpClient:
// CpfHubClient.fs
module CpfValidator.CpfHubClient
open System
open System.Net.Http
open System.Net.Http.Json
open System.Text.RegularExpressions
open System.Threading
open CpfValidator.Types
type CpfHubConfig =
{ ApiKey: string
BaseUrl: string
TimeoutMs: int }
let defaultConfig =
{ ApiKey = "SUA_CHAVE_DE_API"
BaseUrl = "https://api.cpfhub.io"
TimeoutMs = 5000 }
let private limparCpf (cpf: string) =
Regex.Replace(cpf, @"\D", "")
let private mapearErro (statusCode: int) =
match statusCode with
| 400 -> InvalidCpf "CPF com formato inválido"
| 401 -> Unauthorized "Chave de API inválida ou ausente"
| 404 -> NotFound "CPF não encontrado na base de dados"
// Plano sem excedente (inclusive o Grátis): HTTP 403 "Limite de créditos excedido". Limite por minuto: HTTP 429 com Retry-After. Excedente só nos planos pagos; o valor está em /precos.
| code -> ApiError ($"Erro HTTP {code}", code)
let consultarCpf (config: CpfHubConfig) (httpClient: HttpClient) (cpf: string) : Async<CpfResult> =
async {
let cpfLimpo = limparCpf cpf
if cpfLimpo.Length <> 11 then
return Error (InvalidCpf "CPF deve conter exatamente 11 dígitos")
else
let url = $"{config.BaseUrl}/cpf/{cpfLimpo}"
use cts = new CancellationTokenSource(config.TimeoutMs)
try
use request = new HttpRequestMessage(HttpMethod.Get, url)
request.Headers.Add("x-api-key", config.ApiKey)
request.Headers.Add("Accept", "application/json")
let! response =
httpClient.SendAsync(request, cts.Token)
|> Async.AwaitTask
if response.IsSuccessStatusCode then
let! resultado =
response.Content.ReadFromJsonAsync<CpfApiResponse>(cancellationToken = cts.Token)
|> Async.AwaitTask
match resultado with
| null -> return Error (ApiError ("Resposta nula da API", 500))
| r when r.Success ->
match r.Data with
| Some data -> return Ok data
| None -> return Error (ApiError ("Resposta sem dados", 500))
| _ -> return Error (ApiError ("Consulta sem sucesso", 500))
else
return Error (mapearErro (int response.StatusCode))
with
| :? OperationCanceledException ->
return Error (TimeoutError "Timeout ao consultar a API da CPFHub")
| :? HttpRequestException as ex ->
return Error (ApiError ($"Erro de conexão: {ex.Message}", 502))
| ex ->
return Error (ApiError ($"Erro inesperado: {ex.Message}", 500))
}
5. Crie o servidor com Giraffe
Implemente a API REST usando Giraffe sobre ASP.NET Core:
// Program.fs (versão Giraffe)
module CpfValidator.Program
open System
open System.Net.Http
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting
open Giraffe
open CpfValidator.Types
open CpfValidator.CpfHubClient
let private errorToStatus (error: CpfError) =
match error with
| InvalidCpf _ -> 400
| NotFound _ -> 404
| Unauthorized _ -> 401
| RateLimited _ -> 429
| TimeoutError _ -> 504
| ApiError (_, code) -> code
let private errorToMessage (error: CpfError) =
match error with
| InvalidCpf msg -> msg
| NotFound msg -> msg
| Unauthorized msg -> msg
| RateLimited msg -> msg
| TimeoutError msg -> msg
| ApiError (msg, _) -> msg
let consultarCpfHandler (cpf: string) : HttpHandler =
fun next ctx ->
task {
let httpClient = ctx.GetService<IHttpClientFactory>().CreateClient()
let config = defaultConfig
let! resultado = consultarCpf config httpClient cpf |> Async.StartAsTask
match resultado with
| Ok data ->
return! json {| success = true; data = data |} next ctx
| Error error ->
ctx.SetStatusCode(errorToStatus error)
return! json {| success = false; error = errorToMessage error |} next ctx
}
let webApp : HttpHandler =
choose [
GET >=> routef "/api/cpf/%s" consultarCpfHandler
RequestErrors.NOT_FOUND "Recurso não encontrado"
]
let configureApp (app: IApplicationBuilder) =
app.UseGiraffe webApp
let configureServices (services: IServiceCollection) =
services.AddGiraffe() |> ignore
services.AddHttpClient() |> ignore
[<EntryPoint>]
let main args =
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(fun webHost ->
webHost
.Configure(configureApp)
.ConfigureServices(configureServices)
.UseUrls("http://0.0.0.0:5000")
|> ignore)
.Build()
.Run()
0
6. Versão com Suave
Alternativamente, use o Suave para um servidor mais leve e funcional:
// ProgramSuave.fs
module CpfValidator.ProgramSuave
open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful
open Suave.RequestErrors
open System.Net.Http
open Newtonsoft.Json
open CpfValidator.Types
open CpfValidator.CpfHubClient
let private httpClient = new HttpClient()
let consultarCpfSuave (cpf: string) : WebPart =
fun ctx ->
async {
let config = defaultConfig
let! resultado = consultarCpf config httpClient cpf
let jsonResponse =
match resultado with
| Ok data ->
let body = JsonConvert.SerializeObject({| success = true; data = data |})
OK body
| Error error ->
let msg = errorToMessage error
let body = JsonConvert.SerializeObject({| success = false; error = msg |})
BAD_REQUEST body
return! jsonResponse ctx
}
let app : WebPart =
choose [
GET >=> pathScan "/api/cpf/%s" consultarCpfSuave
NOT_FOUND "Recurso não encontrado"
]
let main () =
startWebServer defaultConfig app
7. Teste a integração
dotnet run
curl -X GET http://localhost:5000/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
}
}
8. Boas práticas
-
Discriminated Unions -- Use DUs para modelar todos os estados possíveis de uma operação, incluindo erros. O compilador garante tratamento exaustivo.
-
Result -- Use
Result<'T, 'E>para operações que podem falhar, evitando exceções no fluxo principal. -
Async workflows -- Use
async {}para operações assíncronas, mantendo o código funcional e composável. -
Timeout -- Configure timeout via
CancellationTokenSourcepara garantir que requisições não travem, respeitando a latência de ~150 ms da API. -
HttpClientFactory -- No Giraffe, use
IHttpClientFactorypara gerenciar o ciclo de vida doHttpClient. -
LGPD -- A API da CPFHub.io é 100% compatível com a LGPD. Garanta conformidade no tratamento de dados pessoais em sua aplicação F#.
Perguntas frequentes
Como configurar autenticação ao consumir a API de CPF em F#?
A autenticação usa o header x-api-key em cada requisição. No HttpRequestMessage, adicione request.Headers.Add("x-api-key", config.ApiKey) antes de enviar. A chave de API é gerada no painel da CPFHub.io e deve ser lida de uma variável de ambiente ou secret manager — nunca embutida no código-fonte.
Qual a diferença entre usar Giraffe e Suave para expor a rota de consulta de CPF?
O Giraffe funciona como middleware sobre o ASP.NET Core, aproveitando toda a infraestrutura .NET (injeção de dependência, IHttpClientFactory, logging estruturado). O Suave é um servidor web autônomo, mais leve e sem dependências do ASP.NET, adequado para serviços simples ou ambientes com restrição de footprint. Para produção com múltiplos endpoints, Giraffe tende a ser mais fácil de operar.
O que acontece quando o limite de consultas mensais é atingido?
Sim, no Grátis. A cota de 50 créditos mensais, quando acaba, pausa as consultas com HTTP 403 e a mensagem "Limite de créditos excedido". Um plano pago pode continuar se tiver excedente — o valor está na página de preços — e o teto de chamadas por minuto responde HTTP 429 com Retry-After. O plano Pro oferece 1.000 créditos por R$149/mês.
Como o sistema de tipos do F# ajuda no tratamento de erros da integração?
O discriminated union CpfError força que todo ponto de uso trate explicitamente cada caso de falha — InvalidCpf, NotFound, Unauthorized, TimeoutError e ApiError. O compilador do F# gera aviso para pattern matching incompleto, eliminando a possibilidade de estados de erro não tratados chegarem ao cliente.
Conclusão
Integrar a API da CPFHub.io
Cadastre-se em cpfhub.io
CPFHub.io
Pronto para integrar a API?
50 créditos gratuitos para testar agora. Sem cartão de crédito. Acesso imediato à documentação.




