I have been trying to find some examples on how to create a web app using F# with Nancy. Most of the tutorials on the web cover simple "Hello World!" app, only the basics. In this post, I want to go beyond "Hello World" and show real life examples.

F# and Nancy - Where to start ?

Most basic app "Hello World", is simple.

type App() as this =
    inherit NancyModule()
    do
        this.Get.["/"] <- fun _ -> "Hello World!" :> obj
[<EntryPoint>]
let main args =

    let nancy = new Nancy.Hosting.Self.NancyHost(new Uri("http://localhost:" + "8100"))
    nancy.Start()
    while true do Console.ReadLine() |> ignore
    0

Nancy will automatically load up the App class. To those familiar with Nancy on C# it looks almost the same. The only noticeable difference is the lambda function declaration, plus different syntax for inheritance. Also there is this weird smile face ':>' at the end. This is just a up-casting operator. For some reason Get function has to return object. In F# you also use different syntax for list / array indexing. Instead of Get["/"], you need to use Get.["/"]

GET Examples

this.Get.["/Fsharp"] <- fun _ -> "I can into F#" :> obj
this.Get.["/Json"] <- fun _ -> this.Response.AsJson([ "Test" ]) :> obj
this.Get.["/Complex"] <- fun _ -> 
     let response = this.Response.AsJson(["This is my Response"])
     response.ContentType <- "application/json"
     response.Headers.Add("Funky-Header", "Funky-Header-Value")
     response :> obj

POST

this.Post.["/Post/{test}"] <- fun parameters -> 
      let value = (parameters :?> Nancy.DynamicDictionary).["test"]
      let response = this.Response.AsJson([ sprintf "test %O" value ])
      response :> obj

To extract the parameters I had to cast the input param, which is of obj type, to Nancy.DynamicDictonary. It doesn't look great but there is other way.

this.Post.["/Post/{name}"] <- fun parameters -> 
      let response = this.Response.AsJson([ sprintf "name %O" parameters?name ])
      response :> obj

How to achieve syntax like that - parameters?name ?

let (?) (parameters:obj) param =
    (parameters :?> Nancy.DynamicDictionary).[param]

This part of code is creating new "let-bound operator". It hides the casting logic and makes the code look cleaner.

Error Codes

this.Get.["/500"] <- fun _ -> 500 :> obj
this.Get.["/404"] <- fun _ -> 500 :> obj

Views

this.Get.["/View"] <- fun _ ->
    this.View.["View.html"] :> obj

Syntax is simple and looks basically the same as in C#. There is only one little detail. By default Nancy looks for views in /Views folder. Currently in VS F# project there is no way to create folders from within VS. In order to do this I had to manually, create folder, add file and edit *.fsproj file. Hopefully this will be fixed in the future.

<None Include="Views/View.html">
  <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

More details

ModelBinding

In C# syntax, you would normally use the base method Bind to perform the binding. This method is an extension method and in order to have an access to it, I had to include ModelBinding.</p>

The type to bind

And the code to bind to the model.

this.Post.["/Model"] <- fun parameters ->
   let model = this.Bind<TestModel>()
   this.Response.AsJson(model) :> obj

Unfortunately, it won't work like that. My "TestModel" type is missing default parameter-less constructor and Nancy is throwing "No parameter less constructor defined for this object" Exception.

type TestModel = 
   val TestValue1:string
   new () = {
      TestValue1 = ""
   }

There is a constructor, no Exception, but the value is not bound and is always empty. To fix this, I had to go and look through

Nancy code. By default Nancy is binding to property. My val declaration is not a property.

type TestModel() =

   let mutable testValue = ""

   member this.Testvalue1
      with get () = testValue
      and set (value) = testValue <- value

Syntax for properties is a little different, but nothing serious here. I need to make my value mutable so I can modify the state.

The Application OnError pipeline

To modify the Pipeline I had to add include Nancy.Bootstrapper it has IApplicationStartup interface which can be used to hook into OnError pipeline

type AppStartup =
    interface IApplicationStartup with 
            member this.Initialize pipelines = 
                pipelines.OnError.AddItemToStartOfPipeline(new Func<NancyContext, exn, Response>(fun ctx _ -> new Response()))

Nancy will automatically pick-up this class. The syntax for interface implementation is different, a lot. There is no += operator when working with Events and I had to use ADD method. With this example I got an exception "unable to resolve type AppStartup". It was a problem of missing parameter less constructor.

type AppStartup() =
    interface IApplicationStartup with 
            member this.Initialize pipelines = 
                pipelines.OnError.AddItemToStartOfPipeline(new Func<NancyContext, exn, Response>(fun ctx _ -> new Response()))

The End

Those examples are not really showing the power of F#. This power lays in domain-business logic, not in simple endpoint declaration. It is also the OOP approach with F# syntax. There are other web frameworks that have more functional approach. You can check

Fancy which is a nice wrapper around Nancy. In a future, I might do a comparison with some purely functional web framework.

Share