Recently Microsoft Azure has made F# as a first-class citizen to write Azure Functions. As F# is a functional-first programming language, I feel Azure Functions and F# would be a match made in heaven.

In this blog post, you are going to experience a scaled up version of Azure Functions in F# using Suave

What’s in the Function Signatures?

In a functional programming language, we define small functions that do one thing well and then we compose them together to represent the solution. To compose functions, we need to be thoughtful while designing the signature of a function.

Let’s see the signature of an Azure Function in F#

1 2 3 // HttpRequestMessage -> HttpResponseMessage let Run ( req : HttpRequestMessage ) = new HttpResponseMessage ( HttpStatusCode . OK )

The Run function takes a HttpRequestMessage and returns the HttpResponseMessage . This signature is simple, but it has a limitation. The limitation has been showcased in the templates directory of Azure Webjobs SDK

My each C , R , U , D are in different functions. Well, there is nothing wrong here. These templates are suitable for getting started in Azure Functions. But what will you do if you have a requirement to expose CRUD of a resource as an Azure Functions?

One option is to define each part of the CRUD as separate Azure Functions (as defined by the templates). If you choose to go by this, you will have four different endpoints and I am sure your client code will have a hard time to consume these endpoints. In addition to this, you will also need to manage four things to satisfy your one requirement.

The other option is putting the CRUD inside a single function.

1 2 3 4 5 6 7 8 9 10 11 let Run ( req : HttpRequestMessage ) = if req . Method = HttpMethod . Get then // ... else if req . Method = HttpMethod . Post then // ... else if req . Method = HttpMethod . Put then // ... else if req . Method = HttpMethod . Delete then // ... else // ...

Though this approach solves the problem, it comes with another set of challenges. In Object Oriented Programming, we typically use Polymorphism to replace the conditional logic.

Revisiting Function Signature

A request handler looks for some condition to be meet in the incoming HTTP request, and if the predicate succeeds, it modifies the HTTP response.

The signature of the Run function, HttpRequestMessage -> HttpResponseMessage is not completely reflecting the above specification.

Let’s have a look at the limitations of this signature

The Run function doesn’t return the HttpRequestMessage . So if we have multiple handler s we are constrained to use either if else if or Polymorphism.

It doesn’t represent a handler that doesn’t handle the HTTP request. If the HTTP request is GET , the handler for HTTP POST will not modify the HttpResponseMessage

The better signature would have the following to describe a handler in a better way

The handler has to be pure function that takes both Request and Response as it’s parameters

If the handler is not handling the HTTP request, it has to return the unmodified Request and Response along with an indicator saying that it didn’t handle the request.

It’s where the Suave library shines. Suave defines a type called WebPart with the signature to model the handler with the above-said expectations.

1 2 3 4 5 6 7 type HttpContext = { request : HttpRequest response : HttpResult // ... } type WebPart = HttpContext -> Async < HttpContext option >

The Async represents that the WebPart function is a non-blocking asynchronous function and option type models the WebPart which doesn’t handle the HTTP request

The real power of Suave is its set of combinators to manipulate route flow and task composition. You can define an API in Suave that only handles HTTP POST requests and returns Hello as text without typing too much.

1 2 // HttpContext -> Async<HttpContext option> let app = POST >=> OK "HelloSystem.Net.Http

To learn more about the Suave combinators refer my blog post on Building REST API in suave

If you notice the binding app itself is a WebPart (which in turn a function) with the signature HttpContext -> Async<HttpContext option> . So, you can call this function in your application code and project the output of the function to any output medium that you wish.

The Difference

The Azure Functions do an incredible job in helping you to define a part of your system as a function. Suave takes it to the next level by helping you to define your system as function.

In nutshell, Suave complements Azure Functions and helps you to define your system as a Serverless Function

Creating a Suave Adapter

So, to scale up Azure Functions using Suave, all we need is an adapter.

The adapter does the following

Transforms HttpRequestMessage from System.Net.Http to HttpRequest of Suave.Http

Then create an empty Suave’s HttpContext with the above HttpRequest and call the WebPart (that represents your system).

The final step is converting the HttpResult of Suave.Http to HttpResponseMessage of System.Net.Http .

Let’s start from HttpRequestMessage

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 // SuaveAdapter.fsx let SuaveHttpMethod ( httpMethod : System . Net . Http . HttpMethod ) = match httpMethod . Method with | "GET" -> HttpMethod . GET | "POST" -> HttpMethod . POST | "PUT" -> HttpMethod . PUT | "DELETE" -> HttpMethod . DELETE | x -> HttpMethod . OTHER x let SuaveHeaders ( headers : HttpRequestHeaders ) = headers |> Seq . map ( fun h -> ( h . Key , h . Value |> Seq . head )) |> Seq . toList let SuaveRawForm ( content : System . Net . Http . HttpContent ) = async { let! content = content . ReadAsByteArrayAsync () |> Async . AwaitTask return content } let SuaveRawQuery ( requestUri : System . Uri ) = if requestUri . Query . Length > 1 then requestUri . Query . Substring ( 1 ) else "" let NetHeaderValue ( headers : HttpRequestHeaders ) key = headers |> Seq . tryFind ( fun h -> h . Key = key ) |> Option . map ( fun h -> h . Value |> Seq . head ) let SuaveRequest ( req : HttpRequestMessage ) = async { let! content = SuaveRawForm req . Content let host = defaultArg ( NetHeaderValue req . Headers "Host" ) "" return { HttpRequest . empty with url = req . RequestUri `` method `` = SuaveHttpMethod req . Method headers = SuaveHeaders req . Headers rawForm = content rawQuery = SuaveRawQuery req . RequestUri host = host } }

As a convention, I’ve used Net and Suave prefixes in the function name to represent the returning type of System.Net.Http and Suave.Http respectively.

I hope that these functions are self-explanatory, so let’s move on the next step.

To keep it simple, I’ve ignored other HTTP Methods like PATCH, HEAD, etc.

The next step is creating Suave HttpContext

1 2 3 4 let SuaveContext httpRequest = async { let! suaveReq = SuaveRequest httpRequest return { HttpContext . empty with request = suaveReq } }

Then we need to convert HttpResult to HttpResponseMessage

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let NetStatusCode = function | HttpCode . HTTP_200 -> HttpStatusCode . OK | HttpCode . HTTP_201 -> HttpStatusCode . Created | HttpCode . HTTP_400 -> HttpStatusCode . BadRequest | HttpCode . HTTP_404 -> HttpStatusCode . NotFound | HttpCode . HTTP_202 -> HttpStatusCode . Accepted | _ -> HttpStatusCode . Ambiguous let NetHttpResponseMessage httpResult = let content = function | Bytes c -> c | _ -> Array . empty let res = new HttpResponseMessage () let content = new ByteArrayContent ( content httpResult . content ) httpResult . headers |> List . iter content . Headers . Add res . Content <- content res . StatusCode <- NetStatusCode httpResult . status res

To keep it simple, I’ve ignored other HTTP StatusCodes

The final step is putting these functions together and run the WebPart function with the translated HttpContext .

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let SuaveRunAsync app suaveContext = async { let! res = app suaveContext match res with | Some ctx -> return ( NetHttpResponseMessage ctx . response , ctx ) | _ -> let res = new HttpResponseMessage () res . Content <- new ByteArrayContent ( Array . empty ) res . StatusCode <- HttpStatusCode . NotFound return res , suaveContext } let RunWebPartAsync app httpRequest = async { let! suaveContext = SuaveContext httpRequest return ! SuaveRunAsync app suaveContext }

Suave Adapter In Action

Let’s see the Suave Adapter that we created in action.

As already there are two great blog posts by Greg Shackles and Michał Niegrzybowski, I am diving directly into Azure functions in F#.

Let me create a new Azure Function application in Azure with the name “TamAzureFun” and then define the first function HelloSuave .

The function.json of HelloSuave has to be updated with the methods property to support different HTTP request methods.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "disabled" : false , "bindings" : [{ "type" : "httpTrigger" , "name" : "req" , "methods" : [ "get" , "put" , "post" , "delete" ], "authLevel" : "anonymous" , "direction" : "in" },{ "type" : "http" , "name" : "res" , "direction" : "out" }] }

Then add the Suave dependency in project.json

1 2 3 4 5 6 7 8 9 { "frameworks" : { "net46" : { "dependencies" : { "Suave" : "1.1.3" } } } }

Let’s start simply by defining small API (system) that handles different types of HTTP methods.

1 2 3 4 5 6 7 8 9 10 11 12 // app.fsx open Suave open Suave.Successful open Suave.Operators open Suave.Filters let app = choose [ GET >=> OK "GET test" POST >=> OK "POST test" PUT >=> OK "PUT test" DELETE >=> OK "DELETE test" ]

The final step is referring the SuaveAdapter.fsx & app.fsx files in the run.fsx and have fun!

1 2 3 4 5 6 7 8 9 10 11 // run.fsx # load "SuaveAdapter.fsx" # load "app.fsx" open SuaveAdapter open App open System.Net.Http open Suave let Run ( req : HttpRequestMessage ) = let res , _ = RunWebPartAsync app req |> Async . RunSynchronously res

Let’s make some HTTP requests to test our implementation.

Suave is rocking!

Creating a REST API in Azure Functions

We can extend the above example to expose a REST end point!

In Suave a REST API is a function.

Create a new Azure Function HelloREST and add NewtonSoft.Json & Suave dependencies in project.json

1 2 3 4 5 6 7 8 9 10 { "frameworks" : { "net46" : { "dependencies" : { "Suave" : "1.1.3" , "NewtonSoft.Json" : "9.0.1" } } } }

To handle JSON requests and responses, let’s add some combinators

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // Suave.Newtonsoft.Json.fsx open Newtonsoft.Json open Newtonsoft.Json.Serialization open System.Text open Suave.Json open Suave.Http open Suave open Suave.Operators let toJson < ' T > ( o : ' T ) = let settings = new JsonSerializerSettings () settings . ContractResolver <- new CamelCasePropertyNamesContractResolver () JsonConvert . SerializeObject ( o , settings ) |> Encoding . UTF8 . GetBytes let fromJson < ' T > ( bytes : byte [] ) = let json = Encoding . UTF8 . GetString bytes JsonConvert . DeserializeObject ( json , typeof < ' T >) :?> ' T let mapJsonWith < ' TIn , ' TOut > ( deserializer : byte [] -> ' TIn ) ( serializer : ' TOut -> byte [] ) webpart f = request ( fun r -> f ( deserializer r . rawForm ) |> serializer |> webpart >=> Writers . setMimeType "application/json" ) let MapJson < ' T1 , ' T2 > webpart = mapJsonWith < ' T1 , ' T2 > fromJson toJson webpart let ToJson webpart x = toJson x |> webpart >=> Writers . setMimeType "application/json"

Then define the REST api in app.fsx

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 # load "Suave.Newtonsoft.Json.fsx" open Suave open Suave.Successful open Suave.Operators open Suave.Filters open System open Suave.Newtonsoft.Json type Person = { Id : Guid Name : string Email : string } let createPerson person = let newPerson = { person with Id = Guid . NewGuid () } newPerson let getPeople () = [ { Id = Guid . NewGuid () ; Name = "john" ; Email = "j@g.co" } { Id = Guid . NewGuid () ; Name = "mark" ; Email = "m@g.co" }] let getPersonById id = { Id = Guid . Parse ( id ); Name = "john" ; Email = "j@g.co" } |> ToJson ok let deletePersonById id = sprintf "person %s deleted" id |> OK let app = choose [ path "/people" >=> choose [ POST >=> MapJson created createPerson GET >=> ToJson ok ( getPeople () ) PUT >=> MapJson accepted id ] GET >=> pathScan "/people/%s" getPersonById DELETE >=> pathScan "/people/%s" deletePersonById ]

To keep things simple, I am hard coding the values here. It can easily be extended to talk to any data source

Our SuaveAdapter has capable of handling different HTTP methods and but it hasn’t been programmed to deal with different paths.

Here in this example we need to support two separate paths

1 2 GET / people GET / people / feafa5b5 - 304 d - 455 e - b7e7 - 13 a5b3293f77

The HTTP endpoint to call an Azure function has the format

1 https : //{azure-function-app-name}.azurewebsites.net/api/{function-name}

At this point of writing it doesn’t support multiple paths. So, we need to find a workaround to do it.

One way achieving this is to pass the paths as a Header. Let’s name the Header key as X-Suave-URL . Upon receiving the request we can rewrite the URL as

1 https : //{azure-function-app-name}.azurewebsites.net/{header-value-of-X-Suave-URL}

Let’s update SuaveAdapter.fsx to do this

1 2 3 4 5 6 7 8 9 10 11 12 13 let RunWebPartWithPathAsync app httpRequest = async { let! suaveContext = SuaveContext httpRequest let netHeaderValue = NetHeaderValue httpRequest . Headers match netHeaderValue "X-Suave-URL" , netHeaderValue "Host" with | Some suaveUrl , Some host -> let url = sprintf "https://%s%s" host suaveUrl |> System . Uri let ctx = { suaveContext with request = { suaveContext . request with url = url rawQuery = SuaveRawQuery url }} return ! SuaveRunAsync app ctx | _ -> return ! SuaveRunAsync app suaveContext }

The final step is updating the run.fsx file to use this new function

1 2 3 4 5 6 7 8 9 10 # load "SuaveAdapter.fsx" # load "app.fsx" open SuaveAdapter open App open System.Net.Http open Suave let Run ( req : HttpRequestMessage ) = let res , _ = RunWebPartWithPathAsync app req |> Async . RunSynchronously res

Serverless REST API in Action

This blog post is a proof of concept to use Suave in Azure Functions. There are a lot of improvements to be made to make it production ready. I am planning to publish this as a NuGet package based on the feedback from the community.

Update : Suave.Azure.Functions is available now as a Nuget Package

Summary

"F# is a general purpose language, not just a science, or data science language." @tomaspetricek #ndcoslo #fsharp — Bryan Hunter (@bryan_hunter) June 10, 2016

*PLEASE* Microsoft, stop saying #fsharp is great for "financial applications and stuff like that". It's a bloody general purpose language. — Isaac Abraham (@isaac_abraham) June 7, 2016

The complete source code is available in my GitHub repository.

Are you interested in learning more about F#?

I’m delighted to share that I’m running a tutorial at Progressive F# Tutorials 2016, London on Dec 5, 2016. I’m excited to share my experiences with Suave and help developers to understand this wonderful F# library.

The Progressive F# Tutorials offer hands-on learning for every skill set and is led by some of the best experts in F# and functional programming