SignalR + Nancy with F# hosted on Azure

Learn how to build real-time web applications using SignalR with Nancy and F# hosted on Azure. Step-by-step guide with code examples.

5 min read 824 words

What do you feel when you meet your favorite actor? The feeling is wonderful, right? Now, add your favorite actress to the mixture. Have you said WOW??? How about the director? Is your heart still beating?!? How about having dinner with them? Nothing can be better than that.

I'm having kind of the same feeling. It's nothing new that Nancy works quite comfortably with F#. And thanks to Daniel Mohl, we also have templates to get started with Nancy and F#.

Now, I was doing some experiments around it. It was quite good. But how about running it on Owin? Hmm, it works flawlessly. Nothing more on the code side other than a few lines here and there. Then I found a few projects running SignalR with F#.

I've known Nancy for a long time. But SignalR, I don't know anything about it. The only thing I know is that it is super awesome.

So, the first task is to run Nancy on Owin.

Here are simple steps. Using the template, create an asp.net host with a Razor project. Remove the asp.net host and add Owin host using the NuGet package manager.

Now we need to add a startup file.

type Startup() = 
    member x.Configuration(app : Owin.IAppBuilder) =  app.UseNancy() |> ignore
       

[<Microsoft.Owin.OwinStartup(typeof<Startup>)>]
do ()

And add simple Nancy code. The regular one. Nothing fancy.

There is a bootstrap class:

type Bootstrapper() =
    inherit DefaultNancyBootstrapper()
    override x.ApplicationStartup(container:TinyIoCContainer, pipelines:IPipelines) = 
        StaticConfiguration.DisableErrorTraces <- false
        base.ApplicationStartup(container,pipelines)
        ignore()

This is needed to trace errors if anything is there on the Nancy end.

And our index module: type IndexModule() as x = inherit NancyModule() do x.Get.["/"] <- fun _ -> box x.View.["Index"]

Pretty lame, right? But it works.

Now, add SignalR asp.net host NuGet package to the project. As the startup file is already there, there's no need to create another one. Just a minor modification will do. type Startup() = member x.Configuration(app : Owin.IAppBuilder) = app.MapSignalR().UseNancy() |> ignore

That MapSignalR thing is additionally added. I have just followed the Hello World example of the real-time world. Create a chat application. So, here is the hub to broadcast the message to all users.

type ChatHub() as this = 
    inherit Hub()
    member x.send (name : string, msg : string) = this.Clients.All?broadcastMessage (name, msg) |> ignore

Now, copy-paste the code for the HTML part. Here is the code snippet. There's no need to go into detail as of now for the JavaScript part.

<div>
    <p>Here is SignalR working great with Nancy</p>
    <div class="container">
        <input type="text" id="message" />
        <input type="button" id="sendmessage" value="Send" />
        <input type="hidden" id="displayname" />
        <ul id="discussion"></ul>
    </div>

</div>
<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="Scripts/jquery-1.6.4.min.js"></script>
<!--Reference the SignalR library. -->
<script src="Scripts/jquery.signalR-2.0.2.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="signalr/hubs"></script>
<!--Add script to update the page and send messages.-->
<script type="text/javascript">
    $(function () {
        // Declare a proxy to reference the hub.
        var chat = $.connection.chatHub;
        // Create a function that the hub can call to broadcast messages.
        chat.client.broadcastMessage = function (name, msg) {
            // Html encode display name and message.

            var encodedName = $('<div />').text(name).html();
            var encodedMsg = $('<div />').text(msg).html();
            // Add the message to the page.
            $('#discussion').append('<li><strong>' + encodedName
                + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</li>');
        };
        // Get the user name and store it to prepend to messages.
        $('#displayname').val(prompt('Enter your name:', ''));
        // Set initial focus to message input box.
        $('#message').focus();
        // Start the connection.
        $.connection.hub.start().done(function () {
            $('#sendmessage').click(function () {
                // Call the Send method on the hub.
                chat.server.send($('#displayname').val(), $('#message').val());
                // Clear text box and reset focus for next comment.
                $('#message').val('').focus();
            });
        });
    });
</script>

Now, there is one "?" thing before the broadcastMessage function. That is where the dynamic method is getting called. F# does not support dynamic by default, but there are many workarounds available. I personally prefer the Fsharp.dynamic library. It is better than other code snippets I found on other sites. But that is all personal choice—how one likes to handle dynamic functions.

So, it is done. Hit F5 and you are ready to go.

What's the fun of working with the web if I can't publish it? But for now, with F# web templates, it is not possible. And that is a known issue. So, if I like to publish it on the cloud like Azure, I need to give git hooks. That is the easiest thing I can think of. But the road is still not that smooth.

Deployment will break with some weird error. I followed Mark's article to make it work.

I needed to add the following lines to the .fsproj file after its own import tags.

<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" 
        Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" 
        Condition="true" />

And things are working like anything. No issues at all.

Nancy |> Razor |> SignalR |> Azure |> F# |> lots of love.

Please give it a shot. If it breaks somewhere, let me know—I'll try to solve it. Any suggestions to make things even better and more streamlined are always welcome.

UPDATE: Here is my GitHub repo with updated code. Project created with Visual Studio Express 2013. It is also deployed on Azure.

Frequently Asked Questions

What is the main advantage of using SignalR with Nancy and F#?

SignalR enables real-time, bidirectional communication between server and clients, making it perfect for building interactive applications like chat systems. When combined with Nancy's lightweight framework and F#'s functional capabilities, you get a powerful stack for building responsive web applications with minimal overhead.

How do I set up Nancy with OWIN hosting?

Start by creating an ASP.NET project using the Nancy F# template, then remove the ASP.NET host and install the Nancy.Owin NuGet package. Next, create a Startup class that implements the OWIN pipeline configuration with the UseNancy() method. Finally, add a Bootstrapper class that inherits from DefaultNancyBootstrapper to configure your Nancy application settings.

How do I add SignalR to an existing Nancy OWIN application?

Install the Microsoft.AspNet.SignalR NuGet package, then modify your existing Startup class to include MapSignalR() before UseNancy() in the pipeline configuration. Create a Hub class that inherits from SignalR's Hub base class to handle real-time communication, defining methods like send() to broadcast messages to all connected clients.

What is the purpose of the Bootstrapper class in Nancy?

The Bootstrapper class allows you to configure Nancy application-wide settings and dependencies. In this example, it's used to disable error trace masking (StaticConfiguration.DisableErrorTraces) so you can see detailed error messages during development, making debugging easier.

Can I run Nancy and SignalR on Azure?

Yes, the setup described in this post is designed to run on Azure. Since you're using OWIN hosting with standard ASP.NET components, deployment to Azure follows the same process as any other OWIN-based application, making it fully compatible with Azure's hosting environment.

Share this article