curl in .NETPhoto from Pexels

Originally Posted On: https://dev.to/iron-software/how-to-use-curl-in-net-f-56pa

Comprehensive library now available open source to allow the Userland Curl feature from Linux available in F# and .NET 10

F# developers can also use CurlDotNet to execute curl commands directly in their functional code. The library works seamlessly with F#’s async workflows and type system.

Installing CurlDotNet

Add the NuGet package to your F# project:

dotnet add package CurlDotNet

Or in your .fsproj:

  Include="CurlDotNet" Version="1.0.0" />

Basic Usage in F

Here’s a simple example in F#:

open CurlDotNetopen Systemopen System.Threading.Taskslet main argv = async { let! result = Curl.ExecuteAsync("curl https://api.github.com/users/octocat") |> Async.AwaitTask printfn "Status: %d" result.StatusCode printfn "Body: %s" result.Body } |> Async.RunSynchronously 0

Using F# Async Workflows

F#’s async workflows work naturally with CurlDotNet:

open CurlDotNetopen Systemopen System.Threading.Taskslet fetchUser userId = async { let command = sprintf "curl https://api.github.com/users/%s" userId let! result = Curl.ExecuteAsync(command) |> Async.AwaitTask if result.IsSuccess then return Some result.Body else return None }let main argv = async { match! fetchUser "octocat" with | Some body -> printfn "Success: %s" body | None -> printfn "Failed to fetch user" } |> Async.RunSynchronously 0

Working with JSON

F# can work with JSON responses using standard .NET JSON libraries:

open CurlDotNetopen System.Text.Jsontype User = { Login: string Name: string PublicRepos: int}let fetchUser userId = async { let command = sprintf "curl https://api.github.com/users/%s" userId let! result = Curl.ExecuteAsync(command) |> Async.AwaitTask if result.IsSuccess then let user = JsonSerializer.Deserialize<User>(result.Body) return Some user else return None }let main argv = async { match! fetchUser "octocat" with | Some user -> printfn "User: %s" user.Name printfn "Repos: %d" user.PublicRepos | None -> printfn "Failed" } |> Async.RunSynchronously 0

POST Requests

Here’s how to make POST requests in F#:

open CurlDotNetopen System.Text.Jsonlet createPost title body userId = async { let command = sprintf @" curl -X POST https://jsonplaceholder.typicode.com/posts -H 'Content-Type: application/json' -d '{""title"":""%s"",""body"":""%s"",""userId"":%d}' " title body userId let! result = Curl.ExecuteAsync(command) |> Async.AwaitTask return result }let main argv = async { let! result = createPost "My Post" "This is content" 1 if result.IsSuccess then printfn "Created post with status: %d" result.StatusCode } |> Async.RunSynchronously 0

Error Handling

F#’s pattern matching makes error handling elegant:

open CurlDotNetopen CurlDotNet.Exceptionslet safeExecute command = async { try let! result = Curl.ExecuteAsync(command) |> Async.AwaitTask return Ok result with | :? CurlDnsException as ex -> return Error $"DNS error: {ex.Message}" | :? CurlTimeoutException as ex -> return Error $"Timeout: {ex.Message}" | :? CurlHttpException as ex -> return Error $"HTTP {ex.StatusCode}: {ex.Message}" | :? CurlException as ex -> return Error $"Error: {ex.Message}" }let main argv = async { match! safeExecute "curl https://api.github.com/users/octocat" with | Ok result -> printfn "Success: %s" result.Body | Error msg -> printfn "Failed: %s" msg } |> Async.RunSynchronously 0

Using Computation Expressions

You can create a custom computation expression for cleaner syntax:

open CurlDotNetopen Systemtype CurlBuilder() = member _.Yield(result: CurlResult) = result member _.Bind(result: Task<CurlResult>, f) = async { let! res = Async.AwaitTask result return! f res |> Async.AwaitTask } |> Async.StartAsTask member _.Zero() = Task.FromResult(Unchecked.defaultof<CurlResult>)let curl = CurlBuilder()let fetchData = async { let! result = Curl.ExecuteAsync("curl https://api.github.com/users/octocat") |> Async.AwaitTask if result.IsSuccess then printfn "Data: %s" result.Body }

Pipelining and Composition

F#’s pipelining works great with CurlDotNet:

open CurlDotNetlet fetchUser userId = sprintf "curl https://api.github.com/users/%s" userId |> Curl.ExecuteAsync |> Async.AwaitTasklet parseBody (result: CurlResult) = if result.IsSuccess then Some result.Body else Nonelet main argv = async { "octocat" |> fetchUser |> Async.RunSynchronously |> parseBody |> Option.iter (printfn "Body: %s") } |> Async.RunSynchronously 0

Real-World Example: GitHub API Client

Here’s a complete F# example:

open CurlDotNetopen Systemopen System.Text.Jsontype GitHubUser = { Login: string Name: string PublicRepos: int Followers: int}type Repository = { Name: string FullName: string Private: bool Description: string option}let githubApi token endpoint = async { let command = sprintf @" curl %s -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token %s' " endpoint token let! result = Curl.ExecuteAsync(command) |> Async.AwaitTask if result.IsSuccess then return Ok result.Body else return Error $"HTTP {result.StatusCode}" }let getUser token = async { match! githubApi token "https://api.github.com/user" with | Ok body -> let user = JsonSerializer.Deserialize<GitHubUser>(body) return Some user | Error _ -> return None }let getRepos token = async { match! githubApi token "https://api.github.com/user/repos" with | Ok body -> let repos = JsonSerializer.Deserialize<Repository[]>(body) return Some repos | Error _ -> return None }let main argv = let token = Environment.GetEnvironmentVariable("GITHUB_TOKEN") async { match! getUser token with | Some user -> printfn "Logged in as: %s" user.Login printfn "Name: %s" user.Name printfn "Public repos: %d" user.PublicRepos match! getRepos token with | Some repos -> printfn "nRepositories:" repos |> Array.iter (fun repo -> printfn " - %s" repo.FullName) | None -> printfn "Failed to fetch repos" | None -> printfn "Failed to authenticate" } |> Async.RunSynchronously 0

Conclusion

CurlDotNet works beautifully with F#’s functional programming style. The async workflows, pattern matching, and pipelining all make for clean, elegant code when working with HTTP requests.

Try it in your F# project – you’ll find it fits naturally with F#’s idioms!

 

Information contained on this page is provided by an independent third-party content provider. XPRMedia and this Site make no warranties or representations in connection therewith. If you are affiliated with this page and would like it removed please contact [email protected]