Create an HTTP Trigger Azure Function using FSharp

Introduction

Since the upgrade to the 2.0 version of the Azure Functions runtime, .NET Core has been natively supported by the platform. As a result some changes took effect. Most notably, in version 1.0, a template for an F# HttpTrigger function was available. The template was removed in 2.0. However, that does not mean Azure Functions does not support F#. Azure Functions can be built in F# using a .NET Standard Class Library. This writeup provides a detailed walk-through of how to build an Azure Function that processes HTTP requests using F#. The complete code sample can be found on GitHub.

Prerequisites

This solution was built using a Windows PC but should work on Mac and Linux.

Create Solution

Open the command prompt and create a new directory for your solution called "FsHttpTriggerSample".

mkdir FsHttpTriggerSample

Navigate into the new directory and create a solution using the .NET CLI.

cd FsHttpTriggerSample
dotnet new sln

Create Azure Functions Project

Inside the FsHttpTriggerSample solution directory, use the .NET CLI to create a new F# .NET Standard Class Library project.

dotnet new classlib -o FsHttpTriggerSample -lang f#

Add the project to the solution

dotnet sln add FsHttpTriggerSample

Install NuGet Packages

To use Azure Functions, install the Microsoft.Net.Sdk.Functions NuGet package.

Inside the FsHttpTriggerSample project directory, enter the following command.

dotnet add package Microsoft.Net.Sdk.Functions

Create the Azure Function

Prepare Files

Delete the default Library.fs file inside the FsHttpTriggerSample project directory.

del Library.fs

Create a new file called GreetFunction.fs.

type nul > GreetFunction.fs

Configure Files

Open the FsHttpTriggerSample.fsproj file and find the following snippet.

<Compile Include="Library.fs" />

Replace the snippet with the content below.

<Compile Include="GreetFunction.fs" />

Configure Host

At a minimum, Azure Functions requires the runtime version to run. This information is provided by a file called host.json.

Create a new file called host.json inside the FsHttpTriggerSample project directory.

type nul > host.json

Open the host.json file and add the following content

{
    "version": "2.0"
}

Implement Azure Function

Open the GreetFunction.fs file and add the namespace and module for it.

namespace FsHttpTriggerSample

module GreetFunction = 

Below the module definition, add the following open statements:

open Microsoft.AspNetCore.Mvc
open Microsoft.Azure.WebJobs
open Microsoft.AspNetCore.Http
open Newtonsoft.Json
open System.IO
open Microsoft.Extensions.Logging

Define a User type containing a single property called Name.

type User = {
    Name: string
}

The entrypoint of an Azure Function is the Run function. Create a function called Run.

[<FunctionName("Greet")>]
let Run ([<HttpTrigger(Methods=[|"POST"|])>] req:HttpRequest) (log:ILogger) = 

To register an Azure Function, use the FunctionName attribute. In this case, the name of the function is Greet. The Run function takes two parameters, an HttpRequest and an ILogger. Since the binding used by HTTP Trigger functions is HttpTrigger, the request object is annotated with the HttpTrigger attribute. Options such as the accepted methods can be provided through the HttpTrigger attribute. In this case, only POST requests are accepted.

Create an async computation expression inside the Run function.

async {

}

Inside the async expression, add logging to indicate that the function has initialized.

"Running function"
|> log.LogInformation

Below that, get the body of the request.

let! body = 
    new StreamReader(req.Body) 
    |> (fun stream -> stream.ReadToEndAsync()) 
    |> Async.AwaitTask

Then, deserialized the body into an instance of User.

let user = JsonConvert.DeserializeObject<User>(body)

Return a personalized greeting with the user's name.

return OkObjectResult(sprintf "Hello %s" user.Name)

Finally, use the StartAsTask function to start the async expression as a Task.

|> Async.StartAsTask

Once finished, the contents of the GreetFunction.fs should look similar to the following.

namespace FsHttpTriggerSample

module GreetFunction = 

    open Microsoft.AspNetCore.Mvc
    open Microsoft.Azure.WebJobs
    open Microsoft.AspNetCore.Http
    open Newtonsoft.Json
    open System.IO
    open Microsoft.Extensions.Logging

    type User = {
        Name: string
    }

    [<FunctionName("Greet")>]
    let Run ([<HttpTrigger(Methods=[|"POST"|])>] req:HttpRequest) (log:ILogger) = 
        async {
            "Runnning Function"
            |> log.LogInformation

            let! body = 
                new StreamReader(req.Body) 
                |> (fun stream -> stream.ReadToEndAsync()) 
                |> Async.AwaitTask

            let user = JsonConvert.DeserializeObject<User>(body)

            return OkObjectResult(sprintf "Hello %s" user.Name)
        } |> Async.StartAsTask

Run the Function Locally

Build the project by using the build command inside the FsHttpTriggerSample project directory.

dotnet build

Then, navigate to the output directory

cd bin\Debug\netstandard2.0

Use the Azure Functions Core Tools to start the Azure Functions host locally.

func host start

Once the host is initialized, the function is available at the following endpoint http://localhost:7071/api/Greet.

Test the function

Using a REST client like Postman or Insomnia, make a POST request to http://localhost:7071/api/Greet with the following body. Feel free to replace the name with your own.

{
    "Name": "Luis"
}

If successful, the response should look similar to the following output.

Hello Luis

Conclusion

This writeup showed how to create an HTTP Trigger Azure Function using F#. Creating additional functions inside the same project is relatively trivial since the structure of the GreetFunction.fs file can be copied and the logic inside the Run function can be adapted to meet your requirements.

Resources


Send me a message or webmention