Suave Web Services

I’m on this quest to learn F#. It is a multi-year project because of a couple of reasons

  • I don’t spend enough time on it
  • I’m not very bright

Today’s adventure has been using the Suave.io which is a web framework library and web server. If you’re looking to throw up a quick web service to act as a microservice it doesn’t get much lighter weight than Suave. At the same time you can make more complex processes if necessary.

To get started I opened up Visual Studio and did File > New Project. I then jumped to the package manager console and installed Suave and Chiron

Install-Package suave
Install-Pacakge Chiron

Chiron is a JSON serialization and deserialization library like JSON.NET. We’re going to use it for handling transforming our types into JSON for sending over HTTP. You could use the DataContract serializer or JSON.NET but I think we’ve all had enough of annotating properties.

To start in our Program.fs we’ll start up the web server

[<EntryPoint>]
let main argv =
let app =
choose
[ GET >=> choose
[ path "/home" >=> OK "hello from home"
path "/account" >=> accountInfo ]
POST >=> choose
[
path "/home" >=> OK "Hello post to home"
path "/about" >=> OK "Hello post to about"
]
]
let config = { defaultConfig with
bindings =
[ HttpBinding.mk HTTP IPAddress.Loopback 9000us] }
startWebServer config app
0

We start by creating the app and setting up what is, in effect, a routing table. This is a very simple one and in every case but the /account route we’re just returning an HTTP OK response with the content as text. The choose verb is, in effect, doing pattern matching first against the HTTP verb and then against the path. You might also notice the >=> also known as the Kleisli composition operator but you do not need to know category theory to use F# so don’t worry about that. It composes together WebParts which are the basic units of Suave responses. You can think of it as just being a pipeline. We can call it the rocket-zoom operator if that makes you feel better.

Throwing postman at it we get back pretty much the response we expected

Postman

If your web service is super simple then returning information directly from the routing table could work. In most cases, however, you’re going to want something a little more advanced. In our example the /account route calls out to a function. Let’s look at that.

type account = 
    { number: string;
      balance: decimal;
      name: string}
    static member ToJson(x:account) = 
        Json.write "number" x.number
       *> Json.write "balance" x.balance
       *> Json.write "name" x.name


let accountInfo = 
    let acc = { 
        number= "904324-1";
        balance= 33.87m;
        name="Chequing 1"
    } 
    OK (acc |> Json.serialize |> Json.format ) >=> setMimeType "application/json; charset=utf-8"

Here we create an account type which contains some basic information about a bank account. You’ll notice that we’ve also created a static member on the account type called ToJson. This is used to do the JSON serialization. The *> operator is an overloaded operator from Chiron which just wraps the map2 and makes the code a bit more terse if less understandable. The actual accountInfo method creates a new account record and then serializes it out as an argument to the OK web part. We then rocket-zoom that over to setting the mime type and return it.

In postman that works just fine.

Account call

There is lots more power in Suave but this covers enough to get you started and is, frankly, enough that you could create an entire microservice. Suave is nicely cross platform so you don’t even need to be tied to Windows in your microservice.