Building REST Api in Fsharp Using Suave
In the last one month lot of great things happening in fsharp world around suave, a simple web development fsharp library. Scott Hanselman blogged about it, Tomas Petricek kick-started an awesome hands on session and followed up with a great talk on Channel 9. Last but not the least, Tomasz Heimowski authored a clear and crisp book on Suave
I got super excited after learning suave from these resources and started playing with it. One of the great things about the fsharp community is, if you want to improve any existing library all you need is just send a pull request with the feature you would like to have. Yes, it’s as simple as that! It worked for me and I am sure for you too, if you wish.
In this blog post, you are going to learn how to build a REST api using suave. The REST api that we are going to create here follows the standard being used in the StrongLoop, a node.js REST api library. To keep things simple, we are not going to see validation and error handling as part of this post and I will be covering that in an another post.
Let’s get started
Setting up the project
Create a new “F# Console Application” project in Visual Studio with the name SuaveRestApi
and rename Program.fs
to App.fs
. This file is going to contain the application bootstrap logic. Add two more files Db.fs
and RestFul.fs
which would contain database access and restful api implementation code respectively. Ensure these files are in the order as shown in below.
After creating, install the following nuget packages
WebPart
The basic building block of Suave is WebPart. It is an alias of function type HttpContext -> Async<HttpContext option>
. This simple function type actually model whole set of Request and Response model of Http protocol. From the Tomasz Heimowski’s book here is the definition of the WebPart
Based on the http context, we give you a promise (async) of optional resulting http context, where the resulting context is likely to have its response set with regards to the logic of the WebPart itself
A WebPart
represents a http request and response. Since it is a function, we can combine multiple WebPart
s together and build a complex web application which handles multiple requests and responses. This is the beauty of functional programming. You just think in terms of one function at a time and make it work. Once you are done, all you need to do is just gluing them together. Solving problems using this functional abstraction will make things much easier and help you to write less code compare to its Object Oriented version.
The OK
webpart is a very simple one in Suave which takes a string
and returns a http response with the status code 200
and the given string. Add the following code in the App.fs
and run the console app.
1 2 3 4 5 6 7 |
|
The is the simplest Suave application that greets all visitors with the string “Hello, Suave!”. The startWebServer
is a blocking function that takes a configuration (port number, ssl, etc.,) and starts a http web server in the port 8083 upon calling.
The Rest Api that we are going to design here is a WebPart
which will be replacing this OK
webpart.
HTTP GET
Let’s begin by defining a record type representing a restful resource. Open Restful.fs
and update it as below
1 2 3 4 5 6 |
|
This RestResource
is a container representing all the operations on a restful resource. This record type abstracts the actual resource from the rest api web part. This enables the reuse of rest api web part across multiple resources.
Let’s create a function in RestFul.fs
which takes a resource name and this RestResource
and returns a web part representing the restful api.
1 2 3 |
|
We are going to use the following building blocks of the suave library to implement the rest
function.
-
The
path
is a function of type:string -> WebPart
. It means that if we give it a string it will returnWebPart
. Under the hood, the function looks at the incoming request and returnsSome
if the paths match, andNone
otherwise. -
The
GET
is a static inbuiltWebPart
which matches the HTTP GET requests. -
The
>=>
operator composes two WebParts into one by first evaluating theWebPart
on the left, and applying the WebPart on the right only if the first one returnedSome
.
To return a json response we need to change the mime type to “application/json” of the response. There is an inbuilt function in suave to do this but it is using .Net’s DataContractJsonSerializer
. I feel it’s not ideal to use as we need to write decorator attribute DataMember
to serialize the types.
As Newtonsoft.Json provides serialization support for fsharp types without any need of decorating attributes, we will be using them there.
1 2 3 4 5 6 7 8 |
|
As the signature indicates JSON
function takes a generic type, serialize it using Newtonsoft.Json and return the json response. Writers.setMimeType
takes a mime type and a web part returns the web part with the given mime type.
Now we have everything to implement the HTTP GET request
1 2 3 4 |
|
The one caveat here is the path resolution of Suave library. Based on the webpart given, the startWebServer
function configures it’s internal routing table during the application startup and it is static after the application has been started. So, the getAll
webpart will be created only during application startup. To avoid this, we need to wrap the getAll
webpart with a warbler
function which ensures that it is called only when the incoming request matches the given resource path.
1 2 3 4 |
|
Now we have the middleware to create a web part. Let’s create other things and glue them together.
Open Db.fs
file and add the following code
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Since it’s a sample application, I am just using an in memory dictionary to store the details of people. You can easily replace this with any data source. The Person
type represents the People
resource of the rest api.
The next step is wiring the Restful web part with the application. Open App.fs
and update it as below
1 2 3 4 5 6 7 8 9 10 11 12 |
|
That’s it! Now we have the HTTP GET Request up and running
Since we are having no people in our in-memory dictionary, we are getting an empty result here. Let’s add a new person using HTTP POST
HTTP POST
HTTP POST uses the same workflow as HTTP GET. To implement this, we are going to use the following features in Suave.
-
The
choose
function takes a list of WebParts, and chooses the first one that applies (i.e which returnsSome
), or if none WebPart applies, then choose will returnNone
-
The
request
function takes a function of typeHttpRequest -> WebPart
and returns theWebPart
. We will use thisHttpRequest
to get the POST content. -
The
POST
is a static in-builtWebPart
which matches the HTTP POST requests.
The first step in implementing POST request is updating the RestResource
.
1 2 3 4 |
|
Then add the following utility functions in Restful.fs
to get the resource from the HttpRequest
1 2 3 4 5 6 7 |
|
The rawForm
field in the HttpRequest
has the POST content as a byte array, we are just deserializing to a fsharp type.
The next step is updating the rest
function to support POST
1 2 3 4 5 6 7 |
|
Thanks to the awesome function composition feature, we have combined all these tiny functions and implemented a new request and response.
Then update Db.fs
and App.fs
respectively as follows
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 |
|
The HTTP POST function in action
HTTP PUT
As we did for GET & POST we will be starting by updating the RestResource
1 2 3 4 5 |
|
We have added a bit of error handling here. This Update
function tries to update a resource and returns the updated resource if the resource exists. If it didn’t it returns None
To support HTTP PUT we will be using the following from suave library
-
The
BAD_REQUEST
function takes a string and returns a WebPart representing HTTP status code 400 response with given string as it response. -
The
PUT
is a static in-builtWebPart
which matches the HTTP PUT requests.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Then update Db.fs
and App.fs
as usual
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
We have created one more function here updatePersonById
which will be used later.
1 2 3 4 5 |
|
Updating an existing resource
Updating a non-existing resource
HTTP DELETE
Let’s begin by updating the RestResource
1 2 3 4 5 6 |
|
HTTP DELETE is little different from the other implemented requests as we will be retrieving the id of the resource to be deleted from the URL.
For example, DELETE /people/1 will delete a person with the id 1.
To retrieve the id from the url, we will be using the pathScan
function in Suave. This function similar to printf
which takes a PrintfFormat and a function which takes the output of the printfformat and returns the WebPart. You can get more details about it from the suave-music-store book.
In addition to this, we will be using the following from suave
- The
NO_CONTENT
is a staticWebPart
represents the HTTP Response with the status code 204 - The
DELETE
matches the HTTP request of type DELETE
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 |
|
One thing to notice here is we have wrapped the existing choose
function with an another choose
function. It may be little complex to understand but if you understand what each thing mean, it is easier. Here the inner choose
function represents the handler functions for GET, POST & PUT requests having the url “/{resourceName}” and the DELETE
webpart represents the HTTP DELETE handler for the url “/{resourceName}/{resourceId}”.
The outer choose
function chooses one of these based on the incoming request.
Db.fs
1 2 |
|
App.fs
1 2 3 4 5 6 |
|
HTTP GET & HTTP PUT by id
I hope by this time you know how to wire things up in Suave to create an API. Let’s add features for getting and updating resource by id.
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Db.fs
1 2 3 4 5 |
|
App.fs
1 2 3 4 5 6 7 8 |
|
HTTP HEAD
Http HEAD request checks whether the request with the given id is there or not. Its implementation is straight forward.
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 |
|
Db.fs
1
|
|
App.fs
1 2 3 4 5 6 7 8 9 |
|
That’s all.. We have successfully implemented a REST API using Suave
Extending with a new Resource
The beautiful aspect of this functional REST API design we can easily extend it to support other resources.
Here is the rest API implementation of the albums
resource in the music-store application. You can find the source code of MusicStoreDb here.
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 |
|
Conclusion
In the amazing presentation on Functional Programming Design Patterns, Scott Wlaschin had this slide
I wondered how this can be applied in real-time. By creating a rest API using suave I’ve understood this.
Just create functions and combine it to build a bigger system.
What a nice way to develop a system!
You can get the source code associated with this blog post in my GitHub repository.