Como integrar validação de CPF em F# com Giraffe e Suave

Aprenda a integrar validação de CPF em F# usando Giraffe sobre ASP.NET Core e Suave para consumir a API da CPFHub.io.

Redação CPFHub.io
Redação CPFHub.io
··8 min de leitura
Como integrar validação de CPF em F# com Giraffe e Suave

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 ~900ms 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"
    // Nota: a CPFHub.io não retorna 429; ao exceder o plano gratuito,
    // cobra R$0,15 por consulta adicional sem bloquear requisições.
    | 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 CancellationTokenSource para garantir que requisições não travem, respeitando a latência de ~900ms da API.

  • HttpClientFactory -- No Giraffe, use IHttpClientFactory para gerenciar o ciclo de vida do HttpClient.

  • 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?

A CPFHub.io não bloqueia requisições ao atingir o limite do plano. O plano gratuito inclui 50 consultas mensais; acima disso, cada consulta adicional é cobrada a R$0,15 automaticamente. O plano Pro oferece 1.000 consultas por R$149/mês com o mesmo modelo de excedente proporcional.

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