Dealing with HttpClient and HttpRequestMessage in F#

I simply cannot explain how many times I had to read the documentation in order to remember how to do HTTP calls using HttpClient and HttpRequestMessage. Let’s put an end to this madness now!

Defining a Base Address

This is useful if you intend to use the same HttpClient to make multiple calls to the same API. If that’s the case, do not remove the trailing-slash as it will cause problems when concatenating endpoint paths!

let client = new HttpClient()
client.BaseAddress <- "https://example.com/api/"

// joining our endpoint path
let uri = Uri(client.BaseAddress, "login")

GET Request

In order to make GET requests, you need to setup an HttpRequestMessage instance with the desired method and your endpoint Uri.

let message = new HttpRequestMessage(HttpMethod.Get, uri)
// send request
use! response = client.SendAsync(message)
// read json
let! body = response.Content.ReadFromJsonAsync<T>()

Sending JSON on a POST request

This process is a bit more involved as you need to serialize the JSON into a string before sending the message. Fortunately, we have everything we need without relying on external libraries!

let credentials =
    { Username = "gluer"
      Password = "secret" }

// serialize the credentials object into a string
let json =
    let serialized =
        credentials |> JsonSerializer.Serialize

    new StringContent(serialized, Encoding.UTF8, "application/json")

// prepare the request with a POST method and our Uri
let message = new HttpRequestMessage(HttpMethod.Post, uri)
message.Content <- json

// send request
use! response = state.Client.SendAsync(message)

// read json response
let! body = response.Content.ReadFromJsonAsync<T>()

Error handling

Unfortunately, by default, there is no F#-friendly way of handling errors on HTTP calls. However, the good news is that dealing with them is not difficult either!

type ResponseError =
    | NetworkError of Exception
    | TimeoutError of Exception
    | Unknown of Exception
//...
try
    use! responseMessage = httpClient.SendAsync(message)
    let! body = responseMessage.Content.ReadFromJsonAsync<T>()
    return Ok body
with
| :? HttpRequestException as ex ->
    return Error(NetworkError ex)
| :? TaskCanceledException as ex ->
    return Error(TimeoutError ex)
| ex ->
    return  Error(Unknown ex)

Articles from blogs I follow around the net

Variations of the Range kata

In the languages I usually employ. The Range kata is succinct, bordering on the spartan in both description and requirements. To be honest, it's hardly the most inspiring kata available, and yet it may help showcase a few in…

via ploeh blog January 1, 2024

Some notes on NixOS

Hello! Over the holidays I decided it might be fun to run NixOS on one of my servers, as part of my continuing experiments with Nix. My motivation for this was that previously I was using Ansible to provision the server, but then I’d ad hoc installed a bunch…

via Julia Evans January 1, 2024

I found some of my first code! Annotating and reflecting on robotics code from 2009.

In high school, one of my teachers shattered my plans for my life, in the most beautiful way. Most of my life, I'd intended to become a math professional of some sort: a math teacher, when that was all I saw math for; an actuary, when I started to lear…

via ntietz.com blog January 1, 2024

Generated by openring