Getting RESTful with Suave

I wanted to implement some microservices and thought, what’s more micro than REST style functions, executing single functions using a functional language (functional everywhere!). So let’s take a dip into the world of Suave using F#.

Suave is a lightweight, non-blocking, webserver which can run on Linux, OSX and Windows. It’s amazingly simple to get up and running and includes routing capabilities and more. Let’s try it out.

Getting Started

Create an F# application and the run Install-Package Suave via Package Manager Console.

Now, this code (below) is taken from the Suave website.

open Suave
open Suave.Filters
open Suave.Operators
open Suave.Successful

[<EntryPoint>]
let main argv = 

    let app =
        choose
            [ GET >=> choose
                [ path "/hello" >=> OK "Hello GET"
                  path "/goodbye" >=> OK "Good bye GET" ]
              POST >=> choose
                [ path "/hello" >=> OK "Hello POST"
                  path "/goodbye" >=> OK "Good bye POST" ] ]

    startWebServer defaultConfig app

    0 // return an integer exit code

Run your application and then from you favourite web browser, type in either http://localhost:8083/hello and/or http://localhost:8083/goodbye and you should see “Hello GET” and/or “Good bye GET”. From the code you can see the application also supports POST.

Let’s test this using Powershell’s Invoke-RestMethod. Typing Invoke-RestMethod -Uri http://localhost:8083/hello -Method Post and you will see “Hello POST”.

Passing arguments

Obviously invoking a REST style method is great, but what about passing arguments to the service. We’re going to need to add the import open Suave.RequestErrors to support errors. We can read parameters from the commands using HttpRequest queryParam

 let browse =
        request (fun r ->
            match r.queryParam "a" with
            | Choice1Of2 a -> 
                match r.queryParam "b" with
                | Choice1Of2 b -> OK (sprintf "a: %s b: %s" a b)
                | Choice2Of2 msg -> BAD_REQUEST msg
            | Choice2Of2 msg -> BAD_REQUEST msg)

    let app =
        choose
            [ GET >=> choose
                [ path "/math/add" >=> browse ]
            ]

    startWebServer defaultConfig browse

Disclaimer: This is literally my first attempt at such code, there may be a better way to achieve this, but I felt the code was worth recording anyway. So from our preferred web browser we can type http://localhost:8083/math/add?a=10&b=100 and you should see a: 10 b:100.

Passing JSON to our service

We can also pass data in the form of JSON. For example, we’re now going to pass JSON contain two integers to our new service. So first off add the following

open Suave.Json
open System.Runtime.Serialization

Now we’ll create the data contracts for sending and receiving our data using, these will be serialized automatically for us through the function mapLson which will see in use soon. Notice we’re also able to deal with specific types, such as integers in this case (instead of just strings everywhere).

[<DataContract>]
type Calc =
   { 
      [<field: DataMember(Name = "a")>]
      a : int;
      [<field: DataMember(Name = "b")>]
      b : int;
   }

[<DataContract>]
type Result =
   { 
      [<field: DataMember(Name = "result")>]
      result : int;
   }

Finally let’s see how we startup our server. Here we use the mapJson to map our JSON request into the type Calc from here we carry out some function (in this case addition) and the result is returned (type inference turns the result into a Result type).

startWebServer defaultConfig (mapJson (fun (calc:Calc) -> { result = calc.a + calc.b }))

Let’s test this using our Invoke-RestMethod Powershell code. We can create the JSON body for this method in the following way.

Invoke-RestMethod -Uri http://localhost:8083/ -Method Post -Body '{"a":10, "b":20}'

References

Suave Music Store
Invoke-RestMethod
Building RESTful Web Services
Building REST Api in Fsharp Using Suave