Reactive Chat application using ServiceStack and Fable in F#
Build a real-time reactive chat application using ServiceStack and Fable in F#. Learn functional programming with the SAFE stack alternative.
Here is my Birthday blog post, as a part of FSAdvent - 2017. Just like the previous two years, I hope you will enjoy this one too. Comments and corrections are very much welcomed.
Why Chaat? and What is Chaat?
If anyone wanted to show a real time application, then Chat is kind of the to-do list of that type of applications. Now Functional Chat is a very limited title and can't show all the things I wanted to show. So, I have selected Chaat word as a spin or version of the Chat word.
Here also I am showing a slightly different version of SAFE. So, the title is kind of more suitable.
One thing that is very unique to Indian Chaat is that there is no uniqueness in it. You change the city, order the same thing and you will get a different version of it, and that is the heart of it.
Currently that is possible with dotnet core. Be it on any OS, using any editor/IDE, using any Framework—things still work.
So, what I will do here in this post: I will try to make a Simple Chat application by picking up my favorite frameworks or libraries and also giving reasons for that. In the
EpilogueI will try to provide all other options that can be replaced or mixed and matched. So, you can try it to make your version.
Current State of Dotnet Core
Dotnet core is an unbiased version of dotnet. Currently it is at version 2.*.*. It works on every OS and especially with other editors. Also more openly developed. It may not be the best, but credit should be given. So, here I am using that. If you are sticking with Dotnet, be it a big fat company or start up, and if you are not making Desktop applications, then you should be moving to core for sure.
Functional Programming with F#
There are many Microsoft people who may tell you, don't worry about F# and should take care of requirements on hand first. I am saying the same thing: for the sake of requirements you should be using F#. C# may be good and supported by Microsoft wholeheartedly, but as developers we should choose our tools based on the task at hand. And from personal experience of around 10 years in the software industry, few things are already proven for our current time: Functional First / Functional language is already a winner, no matter how many functional features will be added to OOP languages. They are still not like a functional language. OOP languages were never designed for that. They have done what they are designed for, but I am not sure what place they have in the future. Be it ReasonML, ELM, Scala or F#. Statically typed or not, but all will be functional for sure. Another thing is Big projects, be it a server or a client combined with fast pace delivery; these are the base problems for almost every company. Make peace with it and choose accordingly.
Visual Studio Code
While making this project I have used VSCode. Whatever bad karma was earned by Visual Studio, VSCode is leveling it quite well. VSCode with ionide is quite killing it. It is not the beast like its counterpart, but surely gets things done and it is quite faster.
ServiceStack
Servicestack is built with C# and an OOP framework. It was/is a good old alternative to WCF. If WCF is still around? Then it became a better version of WebAPI (WebAPI is more or less inspired by ServiceStack). Currently it is already at V5 and is quite mature and flexible. Also providing way many things out of the box. When there was a vacuum in F# specific web frameworks, Servicestack was kind of the choice of F# people, and there are obvious reasons for that. Not only does it work with F# but it looks way better with it. And the vision of Servicestack was quite futuristic. It is one of the first to force developers to think in messages while doing web development. And cut to 2017: every good development strategy talks about message-driven development on the client side. Take React-Redux, Elm or Fable-Elmish. The Elm Architecture is the way to go for making big applications on the client side.
Take an example here.
Here are DTOs:
[<CLIMutableAttribute>]
type Message = {
Data : string
Color : string
}
[<CLIMutableAttribute>]
type OutputMessages = {
Data : Message []
}
[<CLIMutableAttribute>]
[<Route("/Chat")>]
type InputMessage = {
UserId : int
Created : DateTime
Message : Message
} with interface IReturn<OutputMessages>
And here is the Service:
type ServerEventsServices() =
inherit Service()
member __.Post(request : InputMessage) =
chaatAgent.Post request
{Data = storage.ToArray()} |> box
If you skip little code for configuration, then this may be the simplest way to understand any web service. Everything is a message. Just like that. Skip the Agent line for now.
Fable
Fable is one of many your favorite language to JavaScript transpilers. Fable here is converting quite a mature functional language, F#. I like to quote ReasonML page here, which also suggests Fable as one of the alternatives if not ReasonML. It is easy to use like Elm, and at the same time it is more flexible than it. I know flexibility comes with a cost, but I find it better than banging my head against a rigid framework. It is the best of both worlds.
Elmish
As I mentioned, Elm architecture is the way to move forward if you are making big / fat business applications. From personal experience I can say if you or your team is using AngularJS specifically 1.0 to make big applications, tell them to change or just run away from there.
Elmish is a thin wrapper around React to provide Elm-like architecture without any Redux complexity. It will provide you message-driven architecture to work with.
Here is a comparison between Redux and Elm in a single tweet.
Elm(ish) vs. Redux Workflow 🙄🤫🤔 pic.twitter.com/naAkxQLFaB
— MikeBild (@mikebild) July 20, 2017
Elmish architecture has three main parts: Model -> View -> Update
Model
type [<StringEnum>]SpanCls = Red | Blue | Green | Yellow
type Model = {
LocalStr : string
ServerMessages : Message []
SpanCls : SpanCls
}
type Msg =
| ChangeStr of string
| ChangeColor of SpanCls
| PreparePost
| PostMessage of InputMessage
| SuccessMessages of OutputMessages
| SSESuccessMessages of OutputMessages
| Failed of exn
Forget about the Model part now, as it is just a representation for the view. More important here is Msg. If you can see, of InputMessage and of OutputMessages are both directed from the Server. And all three messages, including Failed, are there to communicate with the Server. It is in direct connection with the Server's DTOs. Other messages are to handle user events from the view. So, every communication is divided into specific messages and handled in State/Update.
View
let root model dispatch =
div [] [
Content.content [] [
ul [] [
for m in model.ServerMessages do
yield
li[][
span [ClassName m.color][str m.data]
]
]
]
br []
br []
p [ClassName (model.SpanCls.ToString())] [str (sprintf "local message %s" model.LocalStr)]
Control.control_div [] [
Radio.radio [CustomClass "red"] [
Radio.input [
Radio.Input.name "color"
Radio.Input.props [
Checked (model.SpanCls = Red)
OnChange (fun _ -> Red |> ChangeColor |> dispatch)
]
]
str "Red"
]
Radio.radio [CustomClass "green"] [
Radio.input [
Radio.Input.name "color"
Radio.Input.props [
Checked (model.SpanCls = Green)
OnChange (fun _ -> Green |> ChangeColor |> dispatch)
]
]
str "Green"
]
Radio.radio [CustomClass "yellow"] [
Radio.input [
Radio.Input.name "color"
Radio.Input.props [
Checked (model.SpanCls = Yellow)
OnChange (fun _ -> Yellow |> ChangeColor |> dispatch)
]
]
str "Yellow"
]
Radio.radio [CustomClass "blue"] [
Radio.input [
Radio.Input.name "color"
Radio.Input.props [
Checked (model.SpanCls = Blue)
OnChange (fun _ -> Blue |> ChangeColor |> dispatch)
]
]
str "Blue"
]
]
Control.control_div [] [
Input.input [
Input.typeIsText
Input.placeholder "AddSomething"
Input.value model.LocalStr
Input.props [
OnChange (fun ev -> !!ev.target?value |> ChangeStr |> dispatch)
]
]
]
Button.button_btn [
Button.onClick (fun _ -> PreparePost |> dispatch)
] [str "Post"]
]
Normally I skip the view part, as HTML is not that interesting. But this is different. It is not only statically typed HTML, but also has Bulma wrapped with statically typed functions. This means if it compiles, you don't have to worry about even CSS typos. Thanks to Fulma. And that is how you write HTML and CSS with confidence.
Update
let update msg model =
match msg with
| ChangeStr s ->
{model with LocalStr = s}, Cmd.none
| ChangeColor s ->
{model with SpanCls = s}, Cmd.none
| PreparePost ->
let inputMessage = dtos.InputMessage.Create()
let message = dtos.Message.Create()
message.color <- model.SpanCls.ToString()
message.data <- model.LocalStr
inputMessage.created <- DateTime.Now.ToString()
inputMessage.userId <- 0.
inputMessage.message <- message
let postCmd = Cmd.ofMsg (PostMessage inputMessage)
model,postCmd
| PostMessage pm ->
let msgPost (msg : InputMessage) =
client.post (msg :> IReturn<OutputMessages>)
let helloCmd (msg: InputMessage) =
Cmd.ofPromise msgPost msg SuccessMessages Failed
let msgCmd = helloCmd pm
model, msgCmd
| SuccessMessages o ->
{model with ServerMessages = o.data.ToArray(); LocalStr = ""}, Cmd.none
| SSESuccessMessages o ->
{model with ServerMessages = o.data.ToArray()}, Cmd.none
| Failed exn ->
model, Cmd.none
A very simple update method. Now closely look at the PreparePost message. That is where the Fable ecosystem shines. I am mutating things, as it is not that strict. And for the fact that mutability is not that bad, but shared mutable state is very bad. I am preparing a post command here and handing it over to another method. And from there it will go forward.
ts2fable
In the above PreparePost and PostMessage messages, there are a few things like dtos and client. That all comes thanks to ts2fable. A library used to convert typescript definition files to F# imports. It is quite magical and super awesome. Currently in beta but works most of the time for around 95% of the code.
And here is code showing how you use it.
let [<Import("*","@servicestack\client")>] SSClient: SSClient.IExports = jsNative
let [<Import("*","./../Imports/IndianChaat.dtos")>] dtos: IExports = jsNative
Simple one-line imports and all library functions are available in your project.
Convert index.d.ts from @servicestack\client to create a Servicestack Import file. And then just pull it into your code. The client library is also provided by Servicestack—another good thing about the framework. And it has quite a complete typed library for TypeScript. So, you use that just like that with Fable.
What about dtos?
You can generate TypeScript dtos using @servicestack\cli's command ts-ref <url> <filename - optional>. Then using tsc -d command create JS and definition files. And then using ts2fable convert to an import file. Then pull it into your project. It seems a little complicated, but it is just a few commands.
So, now you have a typed client library with typed dtos for you. Here are the Dtos:
type [<AllowNullLiteral>] OutputMessages =
abstract data: ResizeArray<Message> with get, set
type [<AllowNullLiteral>] OutputMessagesStatic =
[<Emit "new $0($1...)">] abstract Create: unit -> OutputMessages
type [<AllowNullLiteral>] Message =
abstract data: string with get, set
abstract color: string with get, set
type [<AllowNullLiteral>] InputMessage =
inherit IReturn<OutputMessages>
abstract userId: float with get, set
abstract created: string with get, set
abstract message: Message with get, set
abstract createResponse: unit -> OutputMessages
abstract getTypeName: unit -> string
type [<AllowNullLiteral>] InputMessageStatic =
[<Emit "new $0($1...)">] abstract Create: unit -> InputMessage
There is another way: you can directly share
dtosfrom the server file. That is the benefit of using F# on both client and server sides. Isomorphic F# all the way. But then I can't show the great work done with the ts2fable tool.
AAA - Actor-Agent-Async
If we are talking about big applications, then skipping the scale word will not happen. And still most Enterprise Applications revolve around Design Patterns. But I am pretty sure that for AAA size applications you need an AAA solution. You can use all or any of it. Power and Flexibility are also in the same order as mentioned in the title. This means Actors are more powerful and flexible, while Async is least. Actors have referential transparency, while agents don't. Due to this, Actors are more suitable for Microservice-kind architectures. Using Actors you can offload your heavy processes to another machine without any issue. Also, it's easy to set up clusters with them. That is not possible with agents. I guess Async is pretty much known by everyone. Also, Async is widely used and should be used whenever you are accessing I/O stuff. All will agree that Async is quite necessary, but at the same time difficult to implement correctly. (I am not considering Java here. The syntax is so horrific.)
For a simple case I am using an Agent here, just taking a middle ground.
type Utility() =
static let rand = Random()
static member RandomSleep() =
let ms = rand.Next(1,1000)
Thread.Sleep ms
let storage = new List<Message>()
let blastAgent = Agent<List<Message>>.Start(fun inbox ->
let rec messageLoop() = async {
let! msg = inbox.Receive()
Utility.RandomSleep()
let sse = ServiceStack.ServiceStackHost.Instance.Container.TryResolve<ServiceStack.IServerEvents>()
let blastObject = {Data = (msg.ToArray())}
sse.NotifyChannel("home","cmd.chat", blastObject)
return! messageLoop()
}
messageLoop()
)
let chaatAgent = Agent<InputMessage>.Start(fun inbox ->
let rec messageLoop() = async {
let! msg = inbox.Receive()
Utility.RandomSleep()
storage.Add(msg.Message)
blastAgent.Post storage
return! messageLoop()
}
messageLoop()
)
Agents are async in nature. They always take one message from the queue. So, you don't have to worry about locks or mutability. They run in isolation, so no more stepping on anyone's feet. Basically, in simple words, they are a queue with a processing brain. If you take the line let sse = ServiceStack.ServiceStackHost.Instance.Container.TryResolve<ServiceStack.IServerEvents>(), here I am messaging from server to client using SSE—Server Sent Events. Mighty Servicestack provides support for it out of the box.
And here is the code on the client side.
let subscribe =
let socketSubscription dispatch =
let eventSourceOptions = createEmpty<IEventSourceOptions>
eventSourceOptions.handlers <- createObj [
// "onConnect" ==> fun (sub : ServerEventConnect) -> printfn "onConnect: %A" sub.displayName
// "onJoin" ==> fun (msg: ServerEventJoin) -> printfn "onJoin: %A" msg.displayName
// "onLeave" ==> fun (msg: ServerEventLeave) -> printfn "onLeave: %A" msg.displayName
// "onUpdate" ==> fun (msg : ServerEventUpdate) -> printfn "onUpdate %A" msg.displayName
"onMessage" ==> fun (msg: ServerEventMessage) -> printfn "onMessage %A" msg.json
"chat" ==> fun (msg : OutputMessages) ->
msg |> (SSESuccessMessages >> dispatch)
] |> Some |> Some
let channels = [|"home"; ""|]
SSClient.ServerEventsClient.Create(baseUrl
, new List<string>(channels)
, eventSourceOptions
).start() |> ignore
Cmd.ofSub socketSubscription
More power to Fable and their support for the dynamic nature of JavaScript. If you can see, the above code is a mixture of static and dynamic typing. While I am creating an object using the static type IEventSourceOptions, I am registering handlers using Fable's dynamic support to create a JS object on the fly. And from here it goes back to the update method using dispatch, from there the application loop will take over.
Here is a wonderful article explaining CRDTs that uses AKKA. In my personal opinion, Microservices should be more about logical and process separation instead of team separation. If you are not Google, then you can't and should not match them step by step.
Business Business Business
There are three things important for any Software: Business Business Business. Be it architecture, UX or scale, all boils down to business. For me, I like my compiler to do work for me. I don't want to write more code, as more code means more errors. Also, code should be kind of future-friendly.
It is the reason behind picking up languages and frameworks. In the above use case, I am replying with a message that is not updated. I am sending an old message. By doing this I am not blocking the user for a reply, even not in an async loop. And then I am changing the message under the table when they arrive via SSE. This makes the UX way better as there will always be a reply for the user.
Take any big application like YouTube / Rotten Tomatoes. They are giving ratings for videos or movies. Many users are giving their star ratings. And I am updating average stars based on that. So, here I am not blocking the user and allowing them to continue. Either you can change things on the client or wait for SSE to come with an updated message. The user never bothers unless and until they know that their input has been taken care of.
Another thing is: JavaScript runs everywhere. OK, that is given. You can't escape from it. But that doesn't tie your hands to using it. You can use any damn thing that transpiles to JavaScript. Elmish / Elm architecture pushes you to make big applications using small isolated Lego pieces. It will force you to think in that direction. It may hurt at the start, but once your project reaches a considerable size, you will thank your previous self for this. And no more null, object not found, function is not an object, or object object errors.
As you have seen, Fable is quite flexible in nature, and here I can join forces with the Servicestack client with it. So, I don't even have to give specific URL paths to make requests or decode JSON on this side. It just works without any issue.
All this is great, now what next?
Docker
There are many containers out there, but I am choosing Docker here. I am not biased, but Docker is kind of the front runner these days, and it will make you future-safe (hopefully). Give or take 5-10 years. If you are just starting things out with your project and not sure about how well it will go, run Docker with Dokku. If you are scaling things up, then you can use any Docker-based hosting. And for every other use case, there is Kubernetes. Using Docker has one side effect: you are not locking yourself into any vendor.
My favorite development time benefit is that you don't have to set up every environment. Once done is done. If you are still not using Docker in the development pipeline while developing your application, you are making a big mistake and you should start using it. Yes, for development purposes too. Don't forget to check in your Dockerfile so every team member is testing / running the application against the same environment.
Fake and Paket
All this stuff would not be possible without the twin F# heroes: Fake and Paket. Everything above is good and shiny, but without joining them together, it is not useful, and more importantly, not fun. Fake and Paket do exactly the same.
Paket is way better at being a package manager than NuGet ever will be. And Fake is a build tool that can run anything and everything literally.
While developing this application, I kept them running, and they ran my tests and server code in watch mode while Fable was in hot reload mode.
The above things are mostly inspired / copy-pasted from SAFE stack. Obviously, changing things as per my personal taste, and that makes my development experience so pleasant. Especially, the feedback loop is quite fast, which makes coding more fun. No more F5, no more breakpoints in JavaScript, and trying to debug what the hell
[object object]is.
This is my one and only 2017 post. Hope you enjoyed it. I am happy to have feedback either here or just tweet it to me.
Complete code for this you can find at my GitHub repo.
Don't forget to read the list below for other alternatives. If I missed anything, let me know and I'll update it.
Epilogue
- F# - If you are looking for an alternative, C# is a good option, but you should go for Q#.
- Server side frameworks - Suave—kind of the default with SAFE, Giraffe if you like ASP.NET Core, Freya—crazy fast option
- Honorable mention for Server Side Frameworks - NancyFx—can't miss this sweet framework
- Fable alternatives - ReasonML, Elm, OCaml, PureScript, ClojureScript
- Elmish alternatives - React - Redux, Elm, Vue - Redux
- Actor Frameworks - Akka, Proto-Actor
- Async Framework - Hopac
- Fake & Paket alternatives - NuGet & .sln file if you are seriously not happy with your life
- Awesome Fable
There are many people from the F# community I would like to thank for this post. Without them, this would not be possible. But personally, I would like to thank Steffen Forkmann for Fake and Paket. Man, if you are ever coming to India, Chaat is on me.
Frequently Asked Questions
A reactive chat application provides real-time communication updates to users. ServiceStack and Fable in F# are chosen for this project because they enable functional programming practices combined with reactive patterns, allowing developers to build responsive applications that handle real-time interactions efficiently.
F# is a functional-first language that is inherently better suited for handling complex state management and real-time operations common in chat applications. While C# can add functional features, it wasn't designed with functional programming as a primary paradigm, making F# a more natural choice for functional requirements.
The blog presents a slightly different version of the SAFE stack, allowing developers to pick their favorite frameworks and libraries while maintaining compatibility. This approach lets you mix and match components based on your specific needs rather than being locked into a single prescribed stack.
.NET Core is an unbiased, platform-agnostic version of .NET that runs on any operating system and works with various editors and IDEs. If you're not building desktop applications, .NET Core should be your choice due to its open development, cross-platform support, and flexibility compared to traditional .NET Framework.